java与c++相比,在内存的分配与回收方面更加具备“自动化”,似乎我们并不需要了解虚拟机GC与内存分配。然而当需要排查各种内存溢出,内存泄露的问题时,当垃圾收集成为系统优化的瓶颈时,我们必须了解JVM的“自动化”技术,以实现对其的监控与调节。
在上一篇博文(java内存区域)中介绍了java内存运行时区域的各个部分,其中虚拟机栈,程序计数器,本地方法栈是线程私有的,这几个区域在编译器其分配的内存空间大小基本上是确定的。然而方法区和java堆则不一样,随着线程的进行,方法的执行以及对象的创建等,其内存空间的大小是不断变化的,所以垃圾回收的作用范围主要是这些区域。
内存的回收主要包括三个方面:
我们可以肯定的是,我们需要回收的对象是那些不再使用的对象,在c++中我们通过析构函数释放对象占用的内存空间。而在java中,这一步是虚拟机帮我们完成的。那么在回收对象的内存空间之前,虚拟机需要判断哪些对象是无用的。下面介绍两种算法:
给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器就减1。当计数器的值为0时就认为这个对象是无用的。
这种算法简单易懂,判定效率也很高,不需要进行复杂的计算,但是其很难解决对象之间相互引用的问题。比如下图:
objA引用B,objB引用A,这时就算我们不在使用A和B了,但由于A和B之间相互引用导致引用计数器的值不为0,通过引用计数算法无法将其回收。正是由于这种算法的弊端,所以主流的java虚拟机都没有选择这种算法来管理内存。
通过一系列的称为“GC ROOT”的对象作为起始点,从这些节点通过其指向其他对象的用用向下搜索,搜索走过的路径称为引用链,当一个对象到“GC ROOT”之间没有引用链就认为其是不可用的。也就是从root开始不可达的节点对象为无用对象
图中对象4,5,6不可达。
在java中可作为GC root的对象包括下面几种:
在JDK1.2之后,java对引用的概念进行了扩充,将引用分为:强引用,软引用,弱引用,虚引用。
不同虚拟机的垃圾收集算法的实现各不相同,这里只介绍几种算法的主要思想。
该算法分为标记
和清除
两个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
不足:
不足:将内存空间缩小为了原来的一半。
标记过程与标记-清除算法
一样,但后续的步骤不是直接对可回收对象进行清理,而是让所有存活的对象都想一端移动,然后直接清理掉端边界以外的内存。
- 新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。
- 老年代:主要存放应用程序中生命周期长的内存对象,或者占用空间比较大的对象。
原文:https://www.cnblogs.com/Mrfanl/p/10423715.html