首先,抛出几个问题:
1. jvm的运行时数据区?每一个区域的作用?各个区域会发生的异常?
2. 一次函数调用,对应的内存变化过程?
3. 常见的垃圾回收算法,以及对应的优缺点?
java运行时数据区
程序计数器:当前线程所执行的字节码的行号指示器
1. 线程私有
2. 存放虚拟机将要执行的下一条指令的地址
3. 唯一没有规定OutOfMemoryError异常的区域
虚拟机方法栈:描述的是java方法执行的内存模型,每一个方法执行时都会创建一个栈帧,一个方法的调用,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
1. 栈桢用于存储:局部变量表,操作栈,动态链接,方法出口等信息。
2. 会出现StackOverFlowError(线程请求的栈深度大于虚拟的所允许的栈深度)和OutOfMemoryError(动态扩展的过程中,无法申请到足够的内存)
堆:所有的对象和数组的分配都在堆上,堆是垃圾回收的主要发生地
1. 从内存回收的角度,可以分为:老年代和新生代(Eden,From Survivor,To Survivor)
2. 可以通过-Xmx和-Xms来控制Heap大小的分配
3. 当堆中没有内存完成实例的分配,并且堆无法动态扩展,会抛出OutOfMemoryError异常
方法区:用于存储,已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
1. 使用-XX:MaxPermSize来调整大小
2. 无法满足内存分配时,会抛出OurOfMemoryError异常
关于运行时常量池
垃圾回收
怎么发现垃圾?
1.引用记数法
垃圾收集器的早期策略。这种方法中,堆中的每一个对象实例都有一个引用计数。当一个对象被创建时,给对象实例分配一个变量,把变量计数设置为1。当其他变量被赋值为这个对象的引用时,计数加1。当对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例被当作垃圾回收。
优点:可以很快的执行,对程序需要不被长时间打断的实时环境比较有利
缺点:无法检测循环引用
2 . 根搜索算法
java中可作为GC Root的对象有:
1.虚拟机栈中引用的对象(本地变量表)
2.方法区中静态属性引用的对象
3. 方法区中常量引用的对象
4.本地方法栈中引用的对象(Native对象)
从GC Root开始,寻找对应的引用节点,找到引用节点之后,继续寻找节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是无用的节点。
怎么回收垃圾?
1. Mark-Sweep算法
标记清除算法,从Root开始扫描,对存活 的对象进行扫描,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。
优点:标记清除算法不需要对对象进行移动,效率上较高,仅对不存活的对象进行清除,在存活对象比较多的情况下极为高效;
缺点:会产生内存碎片;
2. Copying算法
该算法的提出是为了解决句柄的开销和内存碎片问题。将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
优点:如果系统中的垃圾很多,算法需要复制的对象的数量不会很多,因此很高效
缺点:闲置部分空间
3. Mark-Compact算法
标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。同样,对存活的对象进行标记,但在清除时不同。会将所有存活的对象移动到堆一端,对剩下的部分进行清除。
优缺点:会进行对象的移动,成本高,但是解决了内存碎片问题