第九章Java中类生命周期与java垃圾回收机制.ppt
- 格式:ppt
- 大小:164.51 KB
- 文档页数:33
图解JAVA垃圾回收机制(转)摘要: Java技术体系中所提倡的⾃动内存管理最终可以归结为⾃动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存,⽽且这两个问题针对的内存区域就是Java内存模型中的堆区。
关于对象分配内存问题,笔者的博⽂已经阐述了如何划分可⽤空间及其涉及到的线程安全问题,本⽂将结合垃圾回收策略进⼀步给出内存分配规则。
垃圾回收机制的引⼊可以有效的防⽌内存泄露、保证内存的有效使⽤,也⼤⼤解放了Java程序员的双⼿,使得他们在编写程序的时候不再需要考虑内存管理。
本⽂着重介绍了判断⼀个对象是否可以被回收的两种经典算法,并详述了四种典型的垃圾回收算法的基本思想及其直接应⽤——垃圾收集器,最后结合内存回收策略介绍了内存分配规则。
友情提⽰: 为了更好地了解Java的垃圾回收机制,笔者建议读者先要对JVM内存模型有⼀个整体的了解和把握。
鉴于笔者在博⽂中已经深⼊介绍了JVM内存模型,此不赘述。
本⽂内容是基于 JDK 1.6 的,不同版本虚拟机之间也许会有些许差异,但不影响我们对JVM垃圾回收机制的整体把握和了解。
⼀、垃圾回收机制的意义 在笔者的上⼀篇博⽂中提到,JVM 内存模型⼀共包括三个部分:堆 ( Java代码可及的 Java堆和 JVM⾃⾝使⽤的⽅法区)、栈 ( 服务Java⽅法的虚拟机栈和服务Native⽅法的本地⽅法栈 ) 和保证程序在多线程环境下能够连续执⾏的程序计数器。
特别地,我们当时就提到Java堆是进⾏垃圾回收的主要区域,故其也被称为GC堆;⽽⽅法区也有⼀个不太严谨的表述,就是永久代。
总的来说,堆 (包括Java堆和⽅法区)是垃圾回收的主要对象,特别是Java堆。
实际上,Java技术体系中所提倡的⾃动内存管理最终可以归结为⾃动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存,⽽且这两个问题针对的内存区域就是Java内存模型中的堆区。
关于对象分配内存问题,笔者的博⽂已经阐述了如何划分可⽤空间及其涉及到的线程安全问题,本⽂将结合垃圾回收策略进⼀步给出内存分配规则。
Java 垃圾回收机制详解•Java内存模型• 1.内存回收机制:内存回收就是释放掉在内存中已经没用的对象。
2.要判断怎样的对象是没用的对象。
这里有2种方法:– 1.采用标记计数的方法:给内存中的对象给打上标记,对象被引用一次,计数就加1,引用被释放了,计数就减一,当这个计数为0的时候,这个对象就可以被回收了。
当然,这也就引发了一个问题:循环引用的对象是无法被识别出来并且被回收的。
所以就有了第二种方法:– 2.采用根搜索算法:从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的判断完了哪些对象是没用的,这样就可以进行回收了最简单的,就是直接清空那个需要被回收的对象。
但是这又出现了一个问题,就是内存会被分为一块一块的小碎片。
– 3.为了解决这个问题,可以采用第二种方法,就是在之前的基础上将存活的对象给整理一下,使他们变成一个连续的内存,从而释放出连续的较大的内存空间。
还有一中回收方法就是采用复制的办法:将内存分为2块,一块用来存放对象,另一块用来放着,当存放对象的那块满了以后就将上面存活的对象给复制过来,然后在这块内存上工作,并且将之前的内存清空,当自己这块满了以后再复制回去,如此反复。
比较效率的一中做法是将以上的几种方法给结合起来。
首先将内存分块,分为新生代,老年代和永久代。
永久代用来存放代码,等一些基本不改变的数据,新生代用来存放刚产生的一些对象,新生代又可分为3块。
分别为Edon区,Survivor0,survivor1,刚产生的对象是放在Edon区中,当这个区块放满了以后就将其存活的部分复制到survivor0块中,并且将Edon区中的数据清空,等到survivor0满了就将其中的存活的数据放到survivor1中,清空survivor0,垃圾回收到了一定次数还未被回收的对象,就可以放到老年区。
一般来说,刚才产生的对象大多是要在下一次垃圾回收的时候就要被回收掉的,只有一小部分对象会被保留下来,这些被保留下来的对象都是比较稳定的,所以在老年区中的对象回收方法可以采用整理的方法,而在Edon区等新生代中采用复制的方法比较好。
Java垃圾回收机制(GC)
分类:
1. 什么是GC
2. GC的区域在哪⾥
3. GC的对象是什么
4. GC做了哪些事
需要GC的内存区域
jvm 中,程序记数器、虚拟机栈、本都⽅法栈都是随线程⽽⽣,随线程⽽灭,栈随着⽅法的进⼊和退出做⼊栈和出栈操作,实现了⾃动的内存清理,因此,我们的内存⽴即回收主要集中于java 堆和⽅法区中,在程序运⾏期间,这部分内存的分配和使⽤都是动态的。
GC的对象
需要进⾏回收的对象就是已经没有存活的对象,判断⼀个对象是否存活有两种常⽤的办法:
1. 引⽤计数: 每个对象有⼀个引⽤计数属性,新增⼀个引⽤时计数就加1,引⽤什邡市计数就减1,计数为0时就可以回收。
(⽆法解决对象
互相引⽤的问题。
)
2. 可达分析( Reachability Analysis ): 从 GC Roots开始向下搜索,搜索所⾛过的路径被称为引⽤链。
当⼀个对象到GC Roots没有任何引
⽤链相连时,则证明此对象是不可⽤的。
不可达对象。
GC Roots包括:
1. 虚拟机栈中引⽤的对象。
2. ⽅法区中静态属性实体引⽤对象。
3. ⽅法区中常量引⽤的对象。
4. 本地⽅法栈中JNI引⽤的对象。
什么时候触发GC
1. 程序调⽤System.gc时可以触发
2. 系统⾃⾝来决定GC触发的时机(根据Eden区和From Space区的内存⼤⼩来决定。
当内存⼤⼩不⾜时,则会启动GC线程并停⽌应⽤
线程)
GC做了什么事
主要做了清理对象,整理内存的⼯作。
Java堆分为新⽣代和⽼年代,采⽤了不同的回收⽅式。
Java中的垃圾回收垃圾回收确定对象是否存活?两种确定对象是否存活:引⽤计数算法可达性分析算法引⽤计数算法给对象中添加引⽤计数器,若有⼀个引⽤,则计数值加⼀,若引⽤失效,计数值减⼀。
存在的问题:难以解决循环引⽤问题。
可达性分析算法通过GC Roots作为起始节点,根据引⽤关系向下开始搜索,所有搜索⾛过的路径称为“引⽤链”。
如果⼀个对象没有到达GC Roots的引⽤链,则为不可达,可以认为该对象可以被回收。
GC Roots对象虚拟机栈中引⽤的对象(⽅法中使⽤参数,局部变量,临时变量)⽅法区中类静态属性引⽤的对象⽅法区中常量引⽤的对象(字符串常量池中的引⽤)本地⽅法栈中引⽤的对象虚拟机内部的引⽤(基本数据类型对象的Class对象)被synchronized持有的对象。
引⽤分类强引⽤:引⽤不会被垃圾收集器回收。
通过 new 创建的引⽤。
软引⽤:在内存不⾜时,对于这些进⾏回收。
通过 SoftReference 创建软引⽤。
弱引⽤:对象存活到下⼀次垃圾收集。
通过 WeakReference 创建弱引⽤。
虚引⽤:不影响对象的⽣存周期,⽆法通过虚引⽤获得对象实例。
⽤于在对象被回收时收到系统通知。
回收⽅法区主要是回收:废弃的常量不再使⽤的类型判断⼀个类是否可以被回收的条件该类的所有实例对象已被回收。
(堆中不存在该类及其⼦类的实例)加载该类的类加载器已被回收。
对应的Class对象没有被引⽤。
注:⼤量使⽤反射,动态代理的场景中,需要有类型卸载的能⼒。
垃圾收集算法Java堆的划分:新⽣代:对象创建的区域,每次垃圾收集会回收⼤量的对象。
⽼年代:保存着⽣存周期较长的对象。
垃圾收集分类:Minor GC/Young GC:⽬标为新⽣代的垃圾收集。
Major GC/Old GC:⽬标是⽼年代的垃圾收集。
(CMS)Mixed GC:收集整个新⽣代和部分⽼年代的垃圾收集(G1)三种主要的垃圾收集算法标记-清除算法标记-复制算法标记-整理算法标记-清除算法分为两个阶段:标记处所有需要回收的对象。
全面分析Java的垃圾回收机制Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。
Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过new、newarray、anewarray和multianewarray等指令建立,但是它们不需要程序代码来显式地释放。
一般来说,堆的是由垃圾回收来负责的,尽管JVM规范并不要求特殊的垃圾回收技术,甚至根本就不需要垃圾回收,但是由于内存的有限性,JVM在实现的时候都有一个由垃圾回收所管理的堆。
垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。
垃圾收集的意义在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象;而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。
JVM的一个系统级线程会自动释放该内存块。
垃圾收集意味着程序不再需要的对象是"无用信息",这些信息将被丢弃。
当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。
事实上,除了释放没用的对象,垃圾收集也可以清除内存记录碎片。
由于创建对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。
碎片是分配给对象的内存块之间的空闲内存洞。
碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
垃圾收集能自动释放内存空间,减轻编程的负担。
这使Java 虚拟机具有一些优点。
首先,它能使编程效率提高。
在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。
在用Java语言编程的时候,靠垃圾收集机制可大大缩短时间。
其次是它保护程序的完整性,垃圾收集是Java语言安全性策略的一个重要部份。
垃圾收集的一个潜在的缺点是它的开销影响程序性能。
Java虚拟机必须追踪运行程序中有用的对象,而且最终释放没用的对象。
java垃圾回收机制垃圾收集GC(Garbage Collection)是Java语⾔的核⼼技术之⼀,在Java中,程序员不需要去关⼼内存动态分配和垃圾回收的问题,这⼀切都交给了JVM来处理。
⼀. jvm的内存结构垃圾回收都是基于内存去回收的,因此,先要对内存结构有⼀个⼤概的了解Java内存运⾏时区域⼤概分了三部分其中PC寄存器、java虚拟机栈、本地⽅法栈3个区域是所有线程独有的⼀块区域,随线程⽽⽣,随线程⽽灭。
栈中的栈帧随着⽅法的进⼊和退出⽽有条不紊地执⾏着⼊栈和出栈操作。
每⼀个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这⼏个区域的内存分配和回收都具备确定性,在这⼏个区域内就不需要过多考虑回收的问题,因为⽅法结束或者线程结束,内存⾃然就跟随着回收了。
⽽Java堆和⽅法区则不⼀样,⼀个接⼝中的多个实现类需要的内存可能不⼀样,⼀个⽅法中的多个实现类需要的内存可能不⼀样,⼀个⽅法中的多个分⽀需要的内存也可能不⼀样,只有在程序处于运⾏期间时才能知道会创建哪些对象,这部分内存的分配和回收是动态的,垃圾收集关注的是这部分的内存。
⼆. 内存分配在解垃圾回收之前,得先了解JVM是怎么分配内存的,然后识别哪些内存是垃圾需要回收,最后才是⽤什么⽅式回收。
Java的内存分配原理与C/C++不同,C/C++每次申请内存时都要malloc进⾏系统调⽤,⽽系统调⽤发⽣在内核空间,每次都要中断进⾏切换,这需要⼀定的开销,⽽Java虚拟机是先⼀次性分配⼀块较⼤的空间,然后每次new时都在该空间上进⾏分配和释放,减少了系统调⽤的次数,节省了⼀定的开销,这有点类似于内存池的概念;⼆是有了这块空间过后,如何进⾏分配和回收就跟GC机制有关了。
三. 垃圾检测(标记)算法什么样的对象才是垃圾?如果⼀个对象没有被其他对象所引⽤该对象就是⽆⽤的,此对象就被称为垃圾,其占⽤的内存也就要被销毁。
那么⾃然⽽然的就引出了我们的第⼆个问题,判断对象为垃圾的算法都有哪些?标记垃圾的算法Java中标记垃圾的算法主要有两种,引⽤计数法和可达性分析算法。
Java垃圾回收机制总结java垃圾回收机制释放那些不再被任何活动对象引⽤的java对象从GC Root作为起点,进⾏链路访问,可以达到的是活跃对象,达不到的是不活跃对象,会被回收。
被GC Root引⽤的对象:虚拟机栈中引⽤的对象⽅法区中的类静态属性引⽤的对象⽅法区中常量引⽤的对象本地⽅法栈中JNI中引⽤的对象GC的算法:引⽤计数:引⽤了+1,没有引⽤-1,为0就被回收。
缺点:现在不⽤了;每次对象赋值需要维护引⽤计数器,计数器本⾝也是⼀种消耗;较难处理循环引⽤。
复制:Young区--Old区Young区:Eden,From,To第⼀步:当Eden区满时会触发第⼀次GC,把还活着的对象拷贝到Survivor from区,当Eden'区再次触发GC时,会扫描Eden区和From区,对这两个区域进⾏垃圾回收,还存活着的放到To区,同时把年龄加1。
如果到达⽼年的标准,放到Old区。
⼀般是15.第⼆步:清空eden和survivorFrom,复制之后有交换,谁空谁是To。
第三步:To和From互换。
⼀般交换15次还是存活,就放⼊⽼年代。
(MaxTenuringThreshold决定)。
优点是没有碎⽚。
缺点是空间⽤的多。
标记清除(mark-sweep)先标记出要回收的对象,然后统⼀回收这些对象。
优点:节约空间。
缺点:会有碎⽚。
标记整理压缩(mark-compact)先标记,再次进⾏扫描,并往⼀端滑动存活对象。
优点:没有碎⽚。
缺点:需要移动对象的成本。
耗时间四种主要的垃圾收集器:串⾏,并⾏,并发,GC。
它们的具体实现:1. Serial(串⾏):单线程环境下使⽤⼀个线程进⾏GC。
会Stop-the-world。
新⽣代⽤的复制,⽼年代⽤的标记压缩。
简单⾼效,对单个cpu环境并且没有线程交互的开销,性能⽐较好。
开启⽅式:-xx:+useSerialGC。
默认的⽼年代是SerialOld。
2. ParNew(并⾏):多个GC收集器并⾏⼯作。
Java垃圾回收机制对于Java垃圾回收机制,这个很久前就学过,并且理解过了,好长时间不⽤,就丢到回⽖洼岛了,这⾥还是记下,⽅便以后再次查看,不⽤浪费太多时间。
了解Java垃圾回收机制,就要知道Java各个版本的区别,尤其是随着JDK版本的提升,都⽐较以前版本有哪些改进。
最近,尤其是JDK1.7中加⼊了G1,这个是增加的新的回收⽅式,起始在JDK1.6 40左右的版本的时候就已经加⼊实验性的G1了。
线⾯是我转发的博⽂,⾃⼰写没那么多时间,有两篇不错,两篇各有互补点。
第⼀篇:说的⽐较多,但不是太详细垃圾收集GC(Garbage Collection)是Java语⾔的核⼼技术之⼀,之前我们曾专门探讨过Java 7新增的垃圾回收器G1的新特性,但在JVM 的内部运⾏机制上看,Java的垃圾回收原理与机制并未改变。
垃圾收集的⽬的在于清除不再使⽤的对象。
GC通过确定对象是否被活动对象引⽤来确定是否收集该对象。
GC⾸先要判断该对象是否是时候可以收集。
两种常⽤的⽅法是引⽤计数和对象引⽤遍历。
总的来说,可分为两⼤类引⽤计数收集器引⽤计数是垃圾收集器中的早期策略。
在这种⽅法中,堆中每个对象(不是引⽤)都有⼀个引⽤计数。
当⼀个对象被创建时,且将该对象分配给⼀个变量,该变量计数设置为1。
当任何其它变量被赋值为这个对象的引⽤时,计数加1(a = b,则b引⽤的对象+1),但当⼀个对象的某个引⽤超过了⽣命周期或者被设置为⼀个新值时,对象的引⽤计数减1。
任何引⽤计数为0的对象可以被当作垃圾收集。
当⼀个对象被垃圾收集时,它引⽤的任何对象计数减1。
优点:引⽤计数收集器可以很快的执⾏,交织在程序运⾏中。
对程序不被长时间打断的实时环境⽐较有利。
缺点:⽆法检测出循环引⽤。
如⽗对象有⼀个对⼦对象的引⽤,⼦对象反过来引⽤⽗对象。
这样,他们的引⽤计数永远不可能为0.跟踪收集器早期的JVM使⽤引⽤计数,现在⼤多数JVM采⽤对象引⽤遍历。
对象引⽤遍历从⼀组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。