Java面试题-day01运行机制

运行机制

1. 运行机制

1) Java跨平台原理


Java实现跨平台是JVM(Java虚拟机)起的作用。如果是C/C++的编译方式,一旦换了一个平台,那么就需要重新编译一份对应的可执行代码,但是Java则不同,编译好了一份Java字节码,换到不同的平台上时,并不需要重新编译,前提是这些平台上都安装了相应平台的JVM,
JVM不是跨平台的

2) 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?

垃圾回收器通常是作为一个单独的低级别的线程运行,在不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
垃圾回收器不可以马上回收内存,程序员可以手动执行,通知GC运行, 但是Java语言规范并不保证GC一定会执行。

3) 垃圾回收机制

① 分代复制垃圾回收

不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。 在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的 Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。

但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较 短,比如:String对象,由于其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次即可回收。如果每次垃圾回收都是对整个堆空间进行回收,花费时间相对会长,并且生命周期长的对象依旧存在,因此引入分代回收,把不同生命周期的对象 放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。

虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。

② 标记垃圾回收
在使用标记清除算法时,未引用对象并不会被立即回收.取而代之的做法是,垃圾对象将一直累计到内存耗尽为止.当内存耗尽时,程序将会被挂起,垃圾回收开始执行.当所有的未引用对象被清理完毕时,程序才会继续执行。
标记清除算法由两个阶段组成:

标记阶段,标记所有的可访问对象。
收集阶段,垃圾收集算法扫描堆并回收所有的未标记对象。

③ 增量垃圾回收
简单地说,它的存在是为了解决标记清除的长停顿问题。增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。

4) JVM加载class文件的原理机制?

答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,<mark>JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化</mark>。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:

1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;
2)如果类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:
Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

5) Java内存

堆(heap)与栈(stack)
在java中,Main函数就是栈的起始点,也是程序的起始点。程序要运行总是有一个起点的(程序执行的入口)。


概括:
1.栈是运行时的单位 , 而堆是存储的单元。
2.栈解决程序的运行问题,即程序如何执行,或者说如何处理数据,
堆解决的是数据存储的问题,即数据怎么放,放在哪儿。

在java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。
而堆则是所有线程共享的。

疑问一:为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?

 1. 从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。
这种隔离、模块化的思想在软件设计的方方面面都有体现。

 2.堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。
    好处:  a.提供了一种有效的数据交互方式(如:共享内存)
             b.堆中的共享常量和缓存可以被所有栈访问,节省了空间。
 3. 栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。
    由于栈只能向上增长,因此就会限制住栈存储内容的能力,
    而堆不同,堆中的对象是可以根据需要动态增长的,
    因此栈和堆的拆分使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
 4. 面向对象就是堆和栈的完美结合。
   		 其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。
     	但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。
     当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;
    	而对象的行为(方法),就是运行逻辑,放在栈中。
    	我们在编写对象的时候,其实就是编写了数据结构,也编写了处理数据的逻辑。不得不承认,面向对象的设计,确实很美。

疑问二: 堆中存什么?栈中存什么?

 1. 栈存储的信息都是跟当前线程(或程序)相关的信息。(局部变量、程序运行状态、方法、方法返回值)等,
     栈中存的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说是可以动态变化的,但是
     在栈中,一个对象只对应了一个4byte的引用(堆栈分离的好处)。
 2. 堆只负责存储对象信息。

疑问三: 为什么不把基本类型放堆中呢?

 1. 其占用的空间一般是1~8个字节---需要空间比较少,
 2.而且因为是基本类型,所以不会出现动态增长的情况---长度固定,因此栈中存储就够了,如果把它存在堆中是没有什么意义的(还会浪费空间)。

疑问四: java中的参数传递是传值呢?还是传引用?

 对象传递是引用值传递,原始类型数据传递是值传递
 实际上这个传入函数的值是对象引用的拷贝,即传递的是引用的地址值,所以还是按值传递
  堆和栈中,栈是程序运行最根本的东西。程序运行可以没有堆,但是不能没有栈。
  而堆是为栈进行数据存储服务的,堆就是一块共享的内存。
不过,正是因为堆和栈的分离的思想,才使得java的垃圾回收成为可能。
  java中,栈的大小通过-Xss来设置,当栈中存储的数据比较多时,需要适当调大这个值,否则会出现 java.lang.StackOverflowError异常。
全部评论

相关推荐

头像
不愿透露姓名的神秘牛友
04-08 00:50
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务