垃圾收集器与内存分配策略

垃圾收集器与内存分配策略

垃圾收集(Garbage Collection, GC)是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如Scala等)程序员在提升开发效率上获得了惊人的便利。理解GC,对于理解JVM和Java语言有着非常重要的作用。并且当我们需要排查各种内存溢出、内存泄漏问题时,当垃圾收集称为系统达到更高并发量的瓶颈时,只有深入理解GC和内存分配,才能对这些“自动化”的技术实施必要的监控和调节。

GC主要需要解决以下三个问题:

  • 哪些内存需要回收?
  • 什么时候回收?
  • 如何回收?

下面将对这些问题进行一一介绍。

2.1 如何判断对象存活

在堆里存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,首要的就是确定这些对象中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象)。

引用计数算法

引用计数器判断对象是否存活的过程是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是不可能再被使用的。

引用计数算法的实现简单,判定效率也很高,大部分情况下是一个不错的算法。它没有被JVM采用的原因是它很难解决对象之间循环引用的问题。

可达性分析算法

在主流商用程序语言的实现中,都是通过可达性分析(tracing GC)来判定对象是否存活的。

算法的基本思路是:通过一系列的称为“GC Roots”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是GC Roots 到这个对象不可达)时,则证明此对象时不可用的。用下图来加以说明:

0635cbe8.png

作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象。

2.2 各种引用

强引用

一般的Object obj = new Object() ,就属于强引用。被强引用关联的对象不会被回收。

软引用

一些有用但是并非必需,用软引用关联的对象,系统将要发生OOM之前,这些对象就会被回收。

下面的例子中,当程序发生OOM之前,尝试去回收软引用所关联的对象,导致后面获取到的值为null。

public class TestSoftRef {
    
    public static class User{
        public int id = 0;
        public String name = "";
        public User(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + "]";
        }
        
    }
    
    public static void main(String[] args) {

        User u = new User(1,"Vincent");
        SoftReference<User> userSoft = new SoftReference<>(u);
        u = null;//保证new User(1,"Vincent")这个实例只有userSoft在软引用
        
        System.out.println(userSoft.get());
        System.gc();//展示gc的时候,SoftReference不一定会被回收
        System.out.println("AfterGc");
        System.out.println(userSoft.get());//new User(1,"Vincent")没有被回收
        List<byte[]> list = new LinkedList<>();
        
        try {
            for(int i=0;i<100;i++) {
                //User(1,"Vincent")实例一直存在
                System.out.println("********************"+userSoft.get());
                list.add(new byte[1024*1024*1]);
            }
        } catch (Throwable e) {
            //抛出了OOM异常后打印的,User(1,"Vincent")这个实例被回收了
            System.out.println("Throwable********************"+userSoft.get());
        }
        
    }
}

程序输出结果:

Screen Shot 2019-12-19 at 8.52.43 PM.png

弱引用 WeakReference

一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。

下面的例子中,发生gc后,弱引用所关联的对象被回收。

public class TestWeakRef {
    public static class User{
        public int id = 0;
        public String name = "";
        public User(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + "]";
        }
        
    }
    
    public static void main(String[] args) {
        User u = new User(1,"Vincent");
        WeakReference<User> userWeak = new WeakReference<>(u);
        u = null;
        System.out.println(userWeak.get());
        System.gc();
        System.out.println("AfterGc");
        System.out.println(userWeak.get());
        
    }
}

输出结果如下:

Screen Shot 2019-12-19 at 8.56.46 PM.png

虚引用

又称为幽灵引用或者幻影引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象。

为一个对象设置虚引用关联的唯一目的,就是能在这个对象被回收时收到一个系统通知

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj = null;

注意:软引用 SoftReference和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。

2.3 方法区回收

很多人认为方法区没有垃圾回收,Java虚拟机规范中确实说过不要求,而且在方法区中进行垃圾收集的“性价比”较低:在堆中,尤其是新生代,常规应用进行一次垃圾收集可以回收70%~95%的空间,而方法区的效率远低于此。在JDK 1.8中,JVM摒弃了永久代,用元空间来作为方法区的实现,下面介绍的将是元空间的垃圾回收。

元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。

话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值