-
如何判断对象可回收
根据可达性分析算法来判断一个对象是否可回收,可达则不可回收,否则可回收; 从GC Roots对象开始,有引用链,存活,没有则可回收,属于GC Roots类型对象如下: 1.方法中的 参数,局部变量,临时变量 (关心---堆区回收) 2.静态变量(关心---方法区回收) 3.字符串常量的引用 (关心---方法区回收) 4.本地方法(native JNI)引用的对象 5.被同步锁持有的对象 (关心---堆区回收) class Test{ //静态变量 private static Object c=new Object(); public void test(参数){ //局部变量,临时变量 Object o=new Object(); } } 说明: 1.回收不仅仅是针对堆区,回收会分为方法区回收和堆区回收 2.堆区回收一些对象 3.方法区回收字符串常量和类型信息和静态变量
-
引用类型
强引用: 尽管发送oom,也不回收的对象 软引用:内存不足情况下,会回收弱引用对象,内存依然不足,发生oom 弱引用:不管内存是否足够,下一次发生GC都会回收调的对象 虚引用:忽略
-
分代收集算法(针对堆回收)
1.分代收集理论:实际开发当中,对象分为两种:绝大部分对象朝生夕死,少部分对象存活比较长。 基于这个理论,对堆划分代,不同代采用不同回收策略,那就是不同代使用不同回收算法, 最大的提高了回收效率。 2.分代回收类型: 新生代收集:Minor GC (堆分区的年轻代) 老年代收集:Major GC (堆老年代) 混合收集:Mixed GC (堆新生代和部分老年代) 整堆收集:Full GC (整个堆和整个方法区) 3.基本回收算法 标记清除算法:在堆中标记出非存活对象,然后释放掉;在堆中标记出存活对象,把剩下的释放掉,可选择的。 缺点:1.效率不稳定,看存活对象的多少,就要标记多少,内存大的情况下,效率极低 2.碎片化内存,就原地释放,释放完后内存不一定连续 标记复制算法:年轻代对象98%活不过第一轮垃圾回收,也就是98%的对象都是要回收的,把年轻代的堆内存 分为两半,每次只用一块内存,垃圾回收时,直接把存活对象复制到另外一半内存,按顺序 进行分配剩下存活的对象 优点:1.不存在内存碎片化的问题,效率也高 缺点:1.比较浪费内存(后面进行了优化,将年轻代划分为eden和两个survivor,内存比例 为8:1:1,也就是每次只会浪费10%的内存) 总结:年轻代对象的特点,大多数选用这种垃圾回收算法 标记整理算法: 将内存中存活的对象,直接移向内存的另外一端,然后擦除边界以外的部分 优点:1.不存在碎片化问题相对标记清除算法 2.不需要浪费10%的额外空间相对于标记复制算法 总结:1.年轻代的特性,标记复制算法比较适合 2.老年代的特性,标记清算和标记整理算法比较适合;标记清除由于碎片化问题,分配内存成本较高 标记整理不存在碎片化问题,但是要移动内存,分配内存方便,但是移动内存成本高
-
常见分代垃圾收集器
年轻代: serial: 使用标记复制算法,gc线程与应用线程是串行化的,gc时候所有应用线程暂停,直到gc完成 (适合内存比较小的情况下,如果再内存大的情况下,gc停顿太长,很影响用户体验) parNew: 使用标记复制算法,gc线程与应用线程是串行化的,gc时候所有应用线程暂停,直到gc完成 但是这个是serial的多线程版本,多个gc线程进行工作 (适合内存比较大的情况下,因为gc多个线程工作,回收快,停顿时间短) parallel Scavenge:使用标记复制算法,gc线程与应用线程是串行化的,gc时候所有应用线程暂停,直到gc完成 但是这个是serial的多线程版本,多个gc线程进行工作 (和parNew差不多,但是可以根据业务场景,可调整gc时间,让吞吐量达到一定可控) 老年代: serial old:使用标记整理算法,gc线程与应用线程是串行化的,gc时候所有应用线程暂停,直到gc完成 parallel old:使用标记整理算法,gc线程与应用线程是串行化的,gc时候所有应用线程暂停,直到gc完成 但是这个是serial old的多线程版本,多个gc线程进行工作 cms:使用标记清除算法,gc线程可以和用户线程并行,停顿时间短
-
JDK8默认垃圾收集器
设置jvm options:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps Heap PSYoungGen total 76288K, used 5261K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000) eden space 65536K, 8% used [0x000000076ab00000,0x000000076b0234a8,0x000000076eb00000) from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000) to space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000) ParOldGen total 175104K, used 0K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000) object space 175104K, 0% used [0x00000006c0000000,0x00000006c0000000,0x00000006cab00000) Metaspace used 2934K, capacity 4496K, committed 4864K, reserved 1056768K class space used 319K, capacity 388K, committed 512K, reserved 1048576K 总结:jdk8的情况下,使用的垃圾回收parallel Scavenge 和parallel old
-
内存分配策略(jdk默认 PSYoungGen ParOldGen)
1.优先分配到eden区,如果内存不够,发生minor gc,存活对象超过survivor大小,多出的直接转移到老年代 否则,交换到survivor,将空出来的eden内存分配给新进来的对象 2.大对象直接分配到老年代(-XX:PretenureSizeThreshold) 3. 根据对象的年龄进入老年代(-XX:MaxTenuringThreshold) 分配小例子: 代码: //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:PretenureSizeThreshold=6291456 byte[] b1=new byte[_1M*4]; byte[] b2=new byte[_1M*2]; gc日志分析: 1.一开始占用2596k 2.创建了一个数组用了 4096 --->已经使用6692k,eden剩余,1500k 3.创建一个数组用了 2048,---->eden需要内存6692+2048,明显eden内存不够,发生minor gc一次 ----->发现survivor只有1024k,而总存活对象为6692k(担保分配到老年代4104k, survivor分配840k,回收了1584k,eden区留了163k) ----->2048k对象经过gc就可以直接分配到eden了
gc日志
-
jvm内存模型
常见面试题之jvm内存回收和分配策略
最新推荐文章于 2022-11-10 19:42:43 发布