文章目录
- 1、什么是垃圾
- 2、为什么需要垃圾收集
- 3、如何进行垃圾收集
- 3.1、早期垃圾收集
- 3.2、Java垃圾收集机制
- 4、小结
垃圾收集(Garbage Collection,GC)并不是Java语言所独有的,早在1960年,Lisp语言中就已经开始使用内存的动态分配和垃圾收集技术。可见,在很早以前,程序运行中产生的垃圾如何处理就已经引起了开发人员的重视。
1、什么是垃圾
前面已经提过,垃圾收集技术并不是Java语言所独有的,如今垃圾收集技术已经是现代开发语言的标配了。但垃圾收集技术却是Java语言的招牌能力,其优秀的垃圾收集机制极大地提高了开发效率。即使经过长时间的发展,Java的垃圾收集机制仍在不断演进变化,这是因为,不同的设备、不同的应用场景,对垃圾收集机制提出了更高的挑战。想要搞清楚垃圾收集机制,首先要弄清楚第一个问题:什么是垃圾?
在Java官网中,对垃圾的定义为:“An object is considered garbage when it can no longer be reached from any pointer in the running program.”意思是,在运行的程序中,当一个对象没有任何指针指向它时,它就会被视为垃圾。
由此可以看出,判断一个对象是否为垃圾对象的关键标准就是是否有指针指向它。当一个对象没有任何指针指向它时,即说明该对象不再被引用。如果一个对象不被引用之后还继续留在内存中,被占用的空间也无法被其他对象使用,如果这些垃圾对象所占用的空间一直保留至程序结束,随着垃圾对象越来越多,将可能导致内存溢出。对这种垃圾对象的清理就类似于我们熟悉的磁盘碎片整理,通过定时清理磁盘中的垃圾碎片,可以有效提升空间利用率。
那么,如何判断一个对象是否有指针指向它呢?关于这一问题,开发人员有很多探讨,诞生了众多对象存活判定算法。
2、为什么需要垃圾收集
对于高级语言来说,一个基本认知是如果不进行垃圾收集,内存迟早都会被消耗完。因为不断地分配内存空间而不进行回收,就好像不停地生产生活,而从来不打扫垃圾一样。
垃圾对象可能散列在任意位置,它所占用的内存被回收后,就会出现零零散散的空位,这些零散的内存利用率是很低的,当需要申请一个较大对象的内存时,可能出现找不到一整块连续的可用的存储空间,所以垃圾收集不仅是把垃圾对象所占用的内存进行回收,还涉及内存的整理。这就好比生活中的清洁、整理等家务,不仅要把垃圾扔掉,还要对物品进行规整,重新摆放,才能让家里看起来更干净整洁、空间利用率更高。
随着应用程序所应付的业务越来越庞大、复杂,用户越来越多,没有垃圾收集就不能保证应用程序的正常进行。
3、如何进行垃圾收集
3.1、早期垃圾收集
那如何进行垃圾收集呢?在早期的C/C++时代,垃圾收集基本上是手工进行的。开发人员使用new关键字申请内存,使用delete关键字释放内存。以下是C++里面申请和释放内存的代码。
这种方式可以灵活决定内存释放的时间,但是却给开发人员带来了很大的负担。因为开发人员必须在代码中频繁申请内存和释放内存,倘若有一些对象由于程序员的编码疏忽,忘记了释放内存,这些垃圾对象永远没有被清除,随着系统运行时间的不断增长,垃圾对象所耗内存可能持续上升,直至出现内存溢出,造成应用程序崩溃。
在有了垃圾收集机制可以自动回收垃圾对象的内存后,上述代码极有可能变成下面这样,不需要再去手动释放内存,等待回收机制自动处理即可:
MibBridge *pBridge = new cmBaseGroupBridge();pBridge->Register(kDestroy);
现在,除了Java以外,C#、Python、Ruby等语言都使用了自动垃圾收集的思想,这也是未来的发展趋势。可以说,这种自动化的内存分配和垃圾收集的方式已经成为现代开发语言的标配。
3.2、Java垃圾收集机制
Java使用的是自动内存管理机制,有内存分配器和垃圾收集器来代为分配和回收内存。自动内存管理机制使开发人员无须参与内存的分配和回收,将开发人员从繁重的内存管理工作中解放出来,同时降低了内存泄漏和内存溢出的风险。
但是对于Java开发人员来说,自动内存管理就像一个黑匣子,如果过度依赖它,将会弱化开发人员在程序出现内存溢出等问题时定位和解决问题的能力。所以,了解JVM的自动内存分配和垃圾收集机制就显得非常重要,只有在真正了解JVM是如何管理内存后,我们才能够在遇见OutOfMemoryError问题时,快速地根据错误异常日志定位并解决问题。
如下图所示:
Java的垃圾收集机制主要作用于运行时数据区中的堆和方法区(图中的虚线区域)。其中,堆是垃圾收集器的工作重点。
Java的垃圾收集机制中有两个十分重要的概念,也是我们需要重点了解和学习的,分别是垃圾收集算法和分代算法。
在JVM中,垃圾收集算法主要有以下三种:
- 标记-清除算法。
- 复制算法。
- 标记压缩算法。
这三种算法主要解决了将垃圾标记出来之后如何清除的问题。三种算法各有利弊,单独采用其中任何一种算法,都不能取得很好的效果。所以在JVM中,会针对内存的不同区域采用不同的垃圾收集算法,这就是分代算法。具体的内存区域如何划分,不同分区又需要采取何种垃圾收集算法。
4、小结
什么是垃圾?没有任何有效引用指向的对象就是垃圾。为什么要进行垃圾收集?提高内存的利用率,降低内存泄漏和内存溢出的风险,减少程序员的代码负担。收集哪里的垃圾?Java虚拟机运行时堆和方法区内存。谁来进行垃圾收集?垃圾收集器。如何进行垃圾收集?不同的区域使用不同垃圾收集算法的分代处理。