java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > jvm垃圾回收

Java jvm垃圾回收详解

作者:月亮的-影子

这篇文章主要介绍了JVM的垃圾回收总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

常见面试题

image-20211022150704627

1.JVM内存回收和分配

1.1主要的区域?

阈值的计算

如果某个年龄段的大小大于幸存区的一半,那么就取阈值或者是这个年龄最小的那个作为新的阈值升级到老年代

gc测试

场景就是先给eden分配足量的空间,然后再申请大量空间,问题就是幸存区的空间不够用

public class GCTest {
    public static void main(String[] args) {
        byte[] allocation1, allocation2;
        allocation1 = new byte[50900*1024];
        allocation2 = new byte[9500*1024];
    }
}

image-20211022151130599

1.2大对象进入老年代

1.3长期存活的对象进入老年代

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
    //survivor_capacity是survivor空间的大小
    size_t desired_survivor_size = (size_t)((((double)survivor_capacity)*TargetSurvivorRatio)/100);
    size_t total = 0;
    uint age = 1;
    while (age < table_size) {
        //sizes数组是每个年龄段对象大小
        total += sizes[age];
        if (total > desired_survivor_size) {
            break;
        }
        age++;
    }
    uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
    ...
}

1.4主要进行gc的区域

gc的类型

Young Gc:收集新生代的

Old Gc:只收集老年代的

Mixed Gc:新生代和部分老年代

Young Gc

Full Gc

1.5空间分配担保?

2.对象已经死亡?

image-20211022155926623

2.1引用计数法

2.2可达性分析

gc Roots的对象

image-20211022160419101

2.3再谈引用

虚引用、软引用、弱引用的区别?

2.4不可达对象不一定回收

2.5如何判断一个常量是废弃常量?

2.6如果判断一个类没有用?

3.垃圾回收算法

hotspot为什么要区分老年代和新生代?

原因就是不同的存活对象需要不同的垃圾回收算法

跨代收集假说?

如果老年代和新生代互相引用,新生代的年龄就会被拉长。但是为了知道新生代什么时候被gc,这个时候可以给新生代加上一个记忆集(把老年代划分为很多个格子,代表谁引用了我),避免扫描整个老年代

4.垃圾回收器

4.1Serial收集器

4.2ParNew收集器

4.3Parallel Scavenge收集器

4.4SerialOld

4.5Parallel Old收集器

4.6CMS收集器

问题?

内存碎片多对cpu资源敏感

image-20211022163933970

4.7G1收集器

同时满足响应快处理多的问题

特点

image-20211022192746263

补充字符串池的本质

第一个问题是String a="a"的时候做了什么?

第二个问题new String(“a”)发生了什么?

第三个问题intern的原理?

String s1=new String(“a”)

String s2=s1.intern();

很明显s1不等于s2如果上面的问题都清晰知道。s1引用的是堆,而s2引用的是常量池的

第四个问题

String s3=new String(“1”)+new String(“1”);

String s5=s3.intern();

String s4=“11”

那么地方他们相等吗?当然是相等的,s3会把1存入常量池,但是不会吧11存入常量池因为,还没编译出来。调用了intern之后才会把对象存入常量池,而这个时候存入的对象就是s3指向的那个。所以s4指向的也是s3的。如果是s0="11"的话那就不一样了,s3.intern只会返回常量池的对象引用地址,而不是s3的,因为s3是不能重复intern 11进去的。jdk1.6的话那么无论怎么样都是错的,intern是复制一份,而不是把对象存入常量池(因为字符串常量池在方法区,而jdk1.7它在堆所以可以很好的保存s3的引用)

下面的代码正确分析应该是三个true,但是在test里面就会先缓存了11导致false, true,false的问题。

@Test
public void test4(){
    String s3 = new String("1") + new String("1");
    String s5 = s3.intern();
    String s4 = "11";
    System.out.println(s5 == s3);
    System.out.println(s5 == s4);
    System.out.println(s3 == s4);
    System.out.println("======================");
    String s6 = new String("go") +new String("od");
    String s7 = s6.intern();
    String s8 = "good";
    System.out.println(s6 == s7);
    System.out.println(s7 == s8);
    System.out.println(s6 == s8);
}

finalize的原理

public class GCTest {
    static GCTest test;
    public void isAlive(){
        System.out.println("我还活着");
    }
    @Override
    protected void finalize() throws Throwable {
        System.out.println("我要死了");
        test=this;
    }
    public static void main(String[] args) throws InterruptedException {
       test = new GCTest();
        test=null;
        System.gc();
        Thread.sleep(500);
        if(test!=null){
            test.isAlive();
        }else{
            System.out.println("死了");
        }
        test=null;
        System.gc();
        if(test!=null){
            test.isAlive();
        }else{
            System.out.println("死了");
        }
    }
}

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

您可能感兴趣的文章:
阅读全文