垃圾回收 - 小疑问

看懂了gc日志、可能心中还有小小的疑惑?

为什么需要垃圾回收

在内存的动态分配和回收机制十分成熟的情况下、仍然没办法避免排查各种内存溢出、内存泄露的问题, 当垃圾收集称为系统达到更高并发量的瓶颈时、就需要了解gc的细节、对gc进行必要的监控和调节

程序计数器、虚拟机栈、本地方法栈 随线程运行结束而消亡、这几个区域的内存分配和回收具有确定性、不需要过多的考虑内存回收问题
而Java堆和方法区、则在运行时才知道要创建的对象、内存的分配是动态的、垃圾回收主要关注的是这部分的内存

如何判断对象已死?
  1. 引用计数法
    给对象添加一个引用计数器、当有新的引用时、计数器的值+1, 引用失效是、计数器的值-1, 计数器的值为0的对象就是不再有引用的对象
    这种方式的判断效率很高、实现也比较简单、但是不太好判断 循环引用的问题
    A->B, B->A
  1. 可达性分析
    通过 GC Roots 作为分析的起点、从这些节点向下搜索、若一个对象通过任意引用链不可达、则会被判断为可回收对象
可作为 `gc root` 的对象:
1. 虚拟机桟中引用的对象
2. 方法区中静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法桟中JNI(Native方法)引用的对象
为什么gc的过程要stw ?

为了避免分析过程中对象的引用关系还在不断的发生变化、导致分析结果的准确性无法保证
系统停顿下来后如果得知对象引用关系呢 ?
在hotspot的实现中、有一个OopMap的数据结构、在类加载的时候, 它就把对象内什么偏移量位置上是什么类型的数据计算出来、在jit编译的过程中也会在特定的位置记录桟和寄存器哪些位置是引用、这样gc扫描时就可以直接得到了
但是、为什么是特定位置 ?
如果每条指令都生成对应OopMap、会需要大量的额外空间、所以只是在特定位置(安全点safepoint)记录这些信息
notice : 设置标记等待线程运行到安全点再gc解决了大部分问题、但如果线程是sleep或者block状态呢 ?线程无法响应jvm的中断请求、运行到安全点去中断挂起、jvm也不可能得到线程被重新分配CPU时间、so、有了安全区域-safe region来解决
safe region: 在一个代码片段之内、引用关系不会发生变化、在这个区域内的任意位置进行gc都是安全的,线程在进入安全区域的时候、会设置自己进入安全区域的标记、jvm在gc时、会忽略这类线程
在线程离开安全区域时会检测系统是否已经完成了gc、ok则可以继续运行、否则需要等待可以离开安全点的信号

晋升为老年代的判断?
  1. 对象优先在Eden区分配、若Eden区无足够的空间、虚拟机会发起一次Minor GC, 经过一次Minor gc、age+1、达到指定age(-XX:MaxTenuringThreshold指定)时、对象会进入老年代
  2. Minor gc时、无法放入 survivor 区的对象、会通过分配担保机制提前转移到old generation
  3. 大对象直接进入老年代, 为避免提前触发gc 大于 -XX:PreTenureThreshold指定值得对象会提前进入直接在老年代分配
  4. 若survivor区 相同年龄的所有对象的大小的总和>survivor区的一半、则 age >= 该age值得对象可以直接进入老年代、无需等到 -XX:MaxTenuringThreshold 指定年龄值

你可能感兴趣的:(垃圾回收 - 小疑问)