电脑打开任务管理器关不掉进程之后,不小心把expoler结束进程了,再重启电脑后,就蓝屏了

理解 Java 垃圾回收机制 - 文章 - 伯乐在线
& 理解 Java 垃圾回收机制
理解java垃圾回收机制有什么好处呢?作为一个软件工程师,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java应用程序。
这是我个人的主观观点,但我相信一个人精通了GC,往往会是一个更好的Java程序员。如果你对GC感兴趣,那就意味着你有一定大规模应用开发的经验。如果你已经仔细过考虑选择合适的GC算法,这意味着你完全理解你开发的应用程序的功能。当然,这可能不是一个优秀开发者共同标准。然而,很少有人会反对我说的:理解GC是成为一个伟大的Java开发人员的要求。
这是“成为一个Java GC专家”系列文章的第一部分。这次我将讲述GC简介,而在下一篇文章中,我将讨论分析GC状态和来自的GC调优的例子。
本文的目的是用简单的方式向你介绍GC。我希望这篇文章被证明是非常有帮助的。事实上,我的同事已经发表了,它们已经在推特变得很流行。你可以去看看。
回到垃圾收集器,在学习GC前,你应该知道一个技术名词:这个词是“stop-the-world。“ 无论你选择哪种GC算法,Stop-the-world都会发生。Stop-the-world意味着JVM停止应用程序,而去进行垃圾回收。当stop-the-world发生时,除了进行垃圾回收的线程,其他所有线程都将停止运行。被中断的任务将在GC任务完成后恢复执行。GC调优往往意味着减少stop-the-world的时间。
分代垃圾收集
在Java代码中,Java语言没有显式的提供分配内存和删除内存的方法。一些开发人员将引用对象设置为null或者调用System.gc()来释放内存。将引用对象设置为null没有什么大问题,但是调用system.gc()方法会大大的影响系统性能,绝对不能这个干。(谢天谢地,我还没看到任何NHN开发者调用这个方法。)
在Java中,由于开发人员没有在代码中显式删除内存,所以垃圾收集器会去发现不需要(垃圾)的对象,然后删除它们,释放内存。这款垃圾收集器是基于以下两个假设而创建的。(称他们为前提条件更好,而不是假设。)
绝大多数对象在短时间内变得不可达
只有少量年老对象引用年轻对象.
这些假设被称为“弱代假说”。为了发挥这一假设的优势,在HotSpot虚拟机中,物理的将内存分为两个—年轻代(young generation)和老年代(old generation)。
年轻代:新创建的对象都存放在这里。因为大多数对象很快变得不可达,所以大多数对象在年轻代中创建,然后消失。当对象从这块内存区域消失时,我们说发生了一次“minor GC”。
老年代:没有变得不可达,存活下来的年轻代对象被复制到这里。这块内存区域一般大于年轻代。因为它更大的规模,GC发生的次数比在年轻代的少。对象从老年代消失时,我们说“major GC”(或“full GC”)发生了。
我们看一下这幅图。
图 1: GC区 & 数据流
上图中的永久代(permanent generation)也称为“方法区(method area)”,他存储class对象和字符串常量。所以这块内存区域绝对不是永久的存放从老年代存活下来的对象的。在这块内存中有可能发生垃圾回收。发生在这里垃圾回收也被称为major GC。
一些人可能想知道:
一个老年代的对象需要引用年轻代的对象,该怎么办?
为了解决这些问题,老年代中有一个被称为“卡表(card table)”的东西,它是一个512 byte大小的块。每当老年代的对象引用年轻代对象时,这种引用会被记录在这张表格中。当垃圾回收发生在年轻代时,只需对这张表进行搜索以确定是否需要进行垃圾回收,而不是检查老年代中的所有对象引用。这张表格用一个叫做“写闸(write barrier)”的东西进行管理。“写闸”是一种装置,对minor GC有更好性能。虽然因为这种机制,会产生一些时间性能开销,但降低了整体的GC时间。
图2: Card Table结构
年轻代组成部分
为了理解GC,我们学习一下年轻代,对象第一次创建发生在这块内存区域。年轻代分为3块。
2个Survivor区
年轻代总共有3块空间,其中2块为Survivor区。各个空间的执行顺序如下:
绝大多数新创建的对象分配在Eden区。
在Eden区发生一次GC后,存活的对象移到其中一个Survivor区。
在Eden区发生一次GC后,对象是存放到Survivor区,这个Survivor区已经存在其他存活的对象。
一旦一个Survivor区已满,存活的对象移动到另外一个Survivor区。然后之前那个空间已满Survivor区将置为空,没有任何数据。
经过重复多次这样的步骤后依旧存活的对象将被移到老年代。
通过检查这些步骤,如你看到的样子,其中一个Survivor区必须保持空。如果数据存在于两个Survivor区,或两个都没使用,你可以将这个情况作为系统错误的一个标志。
经过多次minor GC,数据被转移到老年代过程如下面的图表所示:
图3: GC前和GC后
请注意,在HotSpot虚拟机中,使用两种技术加快内存的分配。一个被称为“指针碰撞(bump-the-pointer)”,另外一个被称为“TLABs(线程本地分配缓冲)”。
指针碰撞技术跟踪分配给Eden区上最新的对象。该对象将位于Eden 区的顶部。如果之后有一个对象被创建,只需检查Eden区是否有足够大的空间存放该对象。如果空间够用,它将被放置在Eden区,存放在空间的顶部。因此,在创建新对象时,只需检查最后被添加对象,看是否还有更多的内存空间允许分配。然而,如果考虑多线程的环境,则是另外一种情况。为了实现多线程环境下,在Eden 区线程安全的去创建保存对象,那么必须加锁,因此性能会下降。在HotSpot虚拟机中TLABs能够解决这一问题。它允许每个线程在Eden区有自己的一小块私有空间。因为每一个线程只能访问自己的TLAB,所以在这个区域甚至可以使用无锁的指针碰撞技术进行内存分配。
我们已经对年轻代有了一个快速的浏览。你不需要要记住我刚才提到的两种技术。即便你不知道他们,也不会怎么样。但请务必记住:对象第一次被创建发生在Eden区,长期存活的对象被移动到老年代的Survivor区。
当老年代数据满时,基本上会执行一次GC。执行程序根据不同GC类型而变化,所以如果你知道不同类型的垃圾收集器,会更容易理解垃圾回收过程。
在JDK7中,有5种垃圾收集器:
Serial收集器
Parallel收集器
Parallel Old收集器 (Parallel Compacting GC)收集器
Concurrent Mark & Sweep GC
(or “CMS”)收集器
Garbage First (G1) 收集器
其中,serial 收集器一定不能用于服务器端。这个收集器类型仅应用于单核CPU桌面电脑。使用serial收集器会显着降低应用程序的性能。
现在让我们来了解每个收集器类型。
Serial 收集器 (-XX:+UseSerialGC)
我们在前一段的解释了在年轻代发生的垃圾回收算法类型。在老年代的GC使用算法被称为“标记-清除-整理”。
该算法的第一步是在老年代标记存活的对象。
从头开始检查堆内存空间,并且只留下依然幸存的对象(清除)。
最后一步,从头开始,顺序地填满堆内存空间,将存活的对象连续存放在一起,这样堆分成两部分:一边有存放的对象,一边没有对象(整理)。
serial收集器应用于小的存储器和少量的CPU。
Parallel收集器(-XX:+UseParallelGC)
图4: Serial收集器 和 Parallel收集器的差异
从这幅图中,你可以很容易看到Serial收集器 和 Parallel收集器的差异。serial收集器只使用一个线程来处理的GC,而parallel收集器使用多线程并行处理GC,因此更快。当有足够大的内存和大量芯数时,parallel收集器是有用的。它也被称为“吞吐量优先垃圾收集器。”
Parallel Old 垃圾收集器(-XX:+UseParallelOldGC)
Parallel Old收集器是自JDK 5开始支持的。相比于parallel收集器,他们的唯一区别就是在老年代所执行的GC算法的不同。它执行三个步骤:标记-汇总-压缩(mark – summary – compaction)。汇总步骤与清理的不同之处在于,其将依然幸存的对象分发到GC预先处理好的不同区域,算法相对清理来说略微复杂一点。
CMS GC (-XX:+UseConcMarkSweepGC)
图5: Serial GC & CMS GC
CMS垃圾收集器(-XX:+UseConcMarkSweepGC)
如你在上图看到的那样, CMS垃圾收集器比之前我解释的各种算法都要复杂很多。初始标记(initial mark) 比较简单。这一步骤只是查找距离类加载器最近的幸存对象。所以停顿时间非常短。之后的并发标记步骤,所有被幸存对象引用的对象会被确认是否已经被追踪检查。这一步的不同之处在于,在标记的过程中,其他的线程依然在执行。在重新标记步骤会修正那些在并发标记步骤中,因新增或者删除对象而导致变动的那部分标记记录。最后,在并发清除步骤,垃圾收集器执行。垃圾收集器进行垃圾收集时,其他线程的依旧在工作。一旦采取了这种GC类型,由于垃圾回收导致的停顿时间会极其短暂。CMS 收集器也被称为低延迟垃圾收集器。它经常被用在那些对于响应时间要求十分苛刻的应用上。
当然,这种GC类型在拥有stop-the-world时间很短的优点的同时,也有如下缺点:
它会比其他GC类型占用更多的内存和CPU
默认情况下不支持压缩步骤
在使用这个GC类型之前你需要慎重考虑。如果因为内存碎片过多而导致压缩任务不得不执行,那么stop-the-world的时间要比其他任何GC类型都长,你需要考虑压缩任务的发生频率以及执行时间。
最后,我们来学习一下G1类型。
图6: Layout of G1 GC
如果你想要理解G1收集器,首先你要忘记你所理解的新生代和老年代。正如你在上图所看到的,每个对象被分配到不同的网格中,随后执行垃圾回收。当一个区域填满之后,对象被转移到另一个区域,并再执行一次垃圾回收。在这种垃圾回收算法中,不再有从新生代移动到老年代的三部曲。这个类型的垃圾收集算法是为了替代CMS 收集器而被创建的,因为CMS 收集器在长时间持续运行时会产生很多问题。
G1最大的好处是他的性能,他比我们在上面讨论过的任何一种GC都要快。但是在JDK 6中,他还只是一个早期试用版本。在JDK7之后才由官方正式发布。就我个人看来,NHN在将JDK 7正式投入商用之前需要很长的一段测试期(至少一年)。因此你可能需要再等一段时间。并且,我也听过几次使用了JDK 6中的G1而导致Java虚拟机宕机的事件。请耐心的等待它更稳定吧。
下一次我将讨论GC优化相关问题,但是在此之前我要先明确一件事情。假如应用中创建的所有对象的大小和类型都是统一的,在我们公司,这种情下使用的WAS的GC参数 可以是相同的。但是WAS所创建对象的大小和生命周期根据服务以及硬件的不同而不同。换句话说,不能因为某个应用使用的GC参数“A”,就说明同样的参数 也能给其他服务带来最佳效果。而是要因地制宜,有的放矢。我们需要找到适合每个WAS线程的最佳参数,并且持续的监控和优化每个设备上的WAS实例。这并不是我的一家之谈,而是负责Oracle Java虚拟机研发的工程师在 JavaOne 2010上已经讨论过的。
本文中我们简略的介绍了Java的垃圾回收机制,请继续关注我们的后续文章,我们将会讨论如何监控Java GC状态以及GC调优。
另外,我特别推荐一本2011年12月发布的《Java性能》(,也可以通过safari在线阅读),还有在Oracle官网发布的白皮书《Java HotSpotTM虚拟机内存管理》(这本书与Java性能优化不是同一本)
作者Sangmin Lee, NHN公司,性能设计实验室高级工程师。
关于作者:
可能感兴趣的话题
感谢分享。
2.在Eden区发生一次GC后,存活的对象移到其中一个Survivor区。
3.在Eden区发生一次GC后,对象是存放到Survivor区,这个Survivor区已经存在其他存活的对象。
这里有点困惑,查看了原文和其他资料,觉得3应该翻译成:每次GC后,对象会在Survivor区堆积,这个Survivor区已经存在其他存活的对象(经过前面的GC)
但请务必记住:对象第一次被创建发生在Eden区,长期存活的对象被移动到老年代的Survivor区。
这句是说,长期存活的对象,将通过[新生代的]Survivor区被转移到老年代。
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线内容团队正试图以我们微薄的力量,把优秀的原创文章和译文分享给读者,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线java--垃圾回收机制详解-java教程-PHP中文网QQ群微信公众号java--垃圾回收机制详解一 概述 1.垃圾JVM垃圾收集针对的是主要是堆中的垃圾,因为线程启动时在栈中分配空间,线程结束,自动释放空间,不需要实时监控;方法区主要存储类信息以及静态变量与常量,通常在整个程序运行期间都有效,不存在需要回收的对象。垃圾指的是无法被线程访问的对象,一个对象只有对线程可见,可被线程访问,才可用,也可以简单理解为没有任何引用的对象。严格来说,没有任何引用对象的表述缩小了垃圾的范畴,比如在循环引用中,一个对象A引用了另一个对象B,对象B又引用了A,A与B的引用关系仅存在于两者之间,没有任何一个外界对象引用A或者B,那么A与B就无法被纳入程序运行之中,即不能被任何一个线程访问,就成了无用对象,按照定义垃圾的动机,不再被使用的对象即为垃圾,A与B自然就属于垃圾。有时也将垃圾称作无用对象,非垃圾称作存活对象。2.内存泄露无用对象持续占有内存导致内存浪费的现象叫做内存泄露。内存泄露的根本原因是长生命周期的对象使用完毕后依然持有短生命周期对象的引用,比如下面的代码:
public void test02(){
Object obj = new Object();
obj.doSome();//调用了对象中的doSome方法以后,就不再使用该对象,但是依然持有对象的引用obj
......................
}在方法中创建了一个对象,创建对象的目的只是为了访问对象中的方法doSome,方法访问完毕,就不再访问对象,而此时引用变量obj依然指向该对象,既然对象存在引用,该对象就不会被视作垃圾,但对象已经变成无用对象,应该解除引用,以便垃圾回收器回收该对象占用的内存空间,“obj=”解除引用。 3.内存回收垃圾回收器回收的不是对象,而是无用对象占用的内存空间,以使空间可以被再次使用。4.内存碎片在内存分配与回收过程中产生的较小的、不连续的可用空间,如图所示:由于等待回收的空间分散排列,回收完成以后,可用空间被已用空间分割成一个个独立的小单元,由于这些单元体积很小,无法存放大的数据,需要为大的数据另外开辟空间,就造成了内存浪费。二 确定垃圾的算法1.引用计数算法对象创建时给对象添加一个引用计数器,每当有一个变量引用该对象,计数器就加1,变量释放了引用,计数器减1,任何时刻计数器为0,就意味着没有对象引用该对象,对象成了垃圾。引用计数算法的天生缺陷是,无法解决循环引用问题,即循环引用的两个对象即使不再被使用,依然被视作可用对象,不会被垃圾回收器回收。JVM垃圾回收机制采用的不是该算法,而是下面的可达性分析算法。2.可达性分析算法可达性分析算法采用追踪方式判断对象是否存活,任何可被追踪即可到达的对象都是存活对象,不可追踪即不可到达的对象都是无用对象。追踪的起点是当前正在被访问的任何一个对象,从该对象出发,找到该对象引用的对象,依次循环,形成一个引用链,不在引用链上的对象就是不被线程使用的对象,就是垃圾。如下图,左侧形成了一条引用链,链上的所有对象都是存活对象,右侧对象虽然相互之间存在引用关系,脱离线程的引用链,因此是无用对象。这种分析是动态的,而非静态的,随着对象引用关系的变化而变化。三 对象存储划分不同对象的生命周期不同,为了及时回收内存,对生命周期短的对象频繁执行垃圾收集操作,而生命周期长的对象比较稳定,可以长期存在,垃圾收集扫描次数少,因此为了节省开销,降低垃圾收集次数,将不同生命周期的对象分别存储于内存的不同区域,以便采用不同的垃圾收集策略。JVM将对象的存储空间分为三个区:新生代、老年代、永久代。1.新生代设立新生代是为了快速回收生命周期短的对象,所有新生成的对象首先放在新生代中。新生代分为3部分:eden、from survivor、to survivor,空间比例为8:1:1。新创建的对象首先放在eden,执行多次gc(垃圾收集),eden区满了以后,那些还存活的对象会被转移到from survivor区,from survivor满了以后,存活的对象被转移到to survivor,to surviror区满了以后,存活的对象就被提升到老年代。JVM对年轻代中的对象频繁执行gc,绝大多数对象会在年轻代被回收,很少一部分进入老年代。2.老年代老年代中的对象都是经过多次gc存活下来的对象,生命周期较长,比较稳定,因此执行gc操作次数较少。3.永久代永久代也就是方法区,存储的是类信息以及静态变量与常量,生命周期与应用程序相同,一般不需要垃圾收集器处理。四 垃圾收集算法垃圾收集算法是垃圾确认以后实际收集时采用的算法,常见的算法有3种:1.标记-清除方法先标记无用对象,然后回收对象占用的内存空间,会导致内存碎片,基本不采用该算法。2.复制算法将内存空间分为多个区域,垃圾收集时将一个区域内的存活对象全部复制到另一个区域,然后清空该区域,这样就不会产生内存碎片。新生代垃圾收集时采用该算法,从eden的区复制到from survivor区,再复制到to survivor区,因为存活对象较少,复制时占用空间较少。3.标记-整理算法首先标记需要清除的对象,将所有存活对象移动到一端,然后清除所有无用对象。老年代不像新生代,每次gc之后存活对象较多,采用复制算法占用内存较大,采用“标记-整理”算法既节省了内存,又不会导致内存碎片。五 垃圾收集时机不同的代有不同的gc机制:Scavenge GC与Full GC。当新生代eden区域已满,新生成的对象申请空间失败,会触发Scavenge GC,对eden区域进行gc操纵,清除无用对象,腾出空间。Scavenge GC只对新生代起作用。当老年代已满或者持久代已满,或者显式调用了System.gc()方法,会触发Full GC,对所有对象存储区域进行gc,资源耗费较大,应该较少Full GC次数。六 其他方法1.System.gc()告知JVM启动垃圾回收器,垃圾回收器采用守护线程,不一定立即启动,具体何时启动无法控制。另外资源消耗大,一般情况下不要显式调用。2.finalize()对象范围的方法。在JVM确认一个对象无法被访问后调用,只能被调用一次,通常用来释放连接资源。该方法被调用后,垃圾回收器不会立即回收对象占用的空间,因为该方法执行过程中,对象可能重新被访问,而是在下一次gc时,确认对象依然无法被访问,才回收对象占用的空间。七 降低GC开销的措施避免使用静态变量,因为静态变量的生命周期与应用程序相同,即使长期不被使用,依然占据内存空间。资源连接,如OutputStream\InputStream\Connection\Socket,使用完毕,应立即关闭,及时释放资源。尽量不要显式调用System.gc(),因为该方法可能触发Full GC,开销较大。减少临时变量的使用,因为方法执行完毕后,临时变量变成垃圾,大量的临时变量会提高gc次数,增加系统消耗。对象引用完毕后,及时释放引用,以便及时回收内存空间。尽量使用可变对象,降低不可变对象的使用次数。尽量使用基本类型变量代替相应的包装类,因为基本类型变量占用的资源比对应的包装类少得多。分散创建于删除对象,因为集中创建对象,需要大量的空间,很可能触发Full GC,瞬间增大系统消耗。集中删除对象,瞬间出现大量无用对象,也有可能触发Full GC。 参考:
以上就是java--垃圾回收机制详解的详细内容,更多请关注php中文网其它相关文章!807点赞收藏分享:&猜你喜欢12345678910
PHP中文网:独家原创,永久免费的在线,php技术学习阵地!Copyright
All Rights Reserved | 皖B2-QQ群:关注微信公众号工具类服务
编辑部专用服务
作者专用服务
综述java运行中垃圾回收机制
java语言作为一中优秀的编程语言,其优秀之因素一个比较重要的方面就是具有自动垃圾回收机制,垃圾回收可以减少程序员的工作量,提升程序的执行效率.本文对垃圾回收机制做了研究说明.
作者单位:
铜仁幼儿师范高等专科学校,贵州铜仁,554300
年,卷(期):
在线出版日期:
本文读者也读过
相关检索词
万方数据知识服务平台--国家科技支撑计划资助项目(编号:2006BAH03B01)(C)北京万方数据股份有限公司
万方数据电子出版社7922人阅读
java(24)
垃圾回收简介
用户程序(mutator)会修改还堆区中的对象集合,从存储管理器处获取空间,创建对象,还可一引入和消除对已有对象的引用。
当mutator不能“达到”某些对象的时候,这些对象就成了垃圾。
目的:找到不可达的对象,并将这些对象交给跟踪空闲空间的存储管理器,收回他们所占的资源。
一些基本概念
类型安全:任何数据分量的类型都是可确定的。
可以在编译时刻确定数据的类型称为静态类型安全,运行时刻确定称为动态类型。
类型不安拳的语言不适合使用自动垃圾回收。
在java中,除了整型和引用这样的基本类型,所有对象都被分配在堆区而不是栈区。这种设计使得程序员不需要关注变量的生命周期,但代价是产生更多的垃圾。
对任何指针解引用就可以被程序直接访问的数据则为可达的。
局部性原理
& & & & 如果一个程序方位的存储位置很可能将在一个很短的时间段再次被访问,则称这个程序具有时间局部性(Temporal locality)。如果被访问过的存储位置的临近位置很可能在一个很短的时间段内被访问,则该程序具有空间局部性。
& & & & 通常认为程序把90%的时间来执行10%的代码。
几种垃圾回收器的原理
标记-清除收集器
这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。并且,由于它只是清除了那些未标记的对象,而并没有对标记对象进行压缩,导致会产生大量内存碎片,从而浪费内存。
标记-压缩收集器
有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。
复制收集器
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,JVM生成的新对象则放在另一半空间中。GC运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。并且对于指定大小堆来说,需要两倍大小的内存,因为任何时候都只使用其中的一半。
增量收集器
增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾,也可理解为把堆栈分成一小块一小块,每次仅对某一个块进行垃圾收集。这会造成较小的应用程序中断时间,使得用户一般不能觉察到垃圾收集器正在工作。
部分回收原理
通常80%~90%的新分配对象在几百万条指令之内或者再分配了。
分代收集器(Generational garbage coolection)
&它是基于拷贝回收器和部分回收原理。
充分利用大多数对象“英年早逝”的特性的有效方法。
将堆区分成一系列小的区域,用0,1,2......n对它们进行编号,序号越小的区域存放的对象越年轻,对象首先在0区域被创建,填满后垃圾被回收,可达对象被移到1区,每一轮垃圾回收都是针对序号小于等于i的区域进行的,i为当前被填满区域的最高编号。
只要对i进行回收,所有序号小雨i的区域也将要进行垃圾回收,应为较年轻的世代往往包含了较多的垃圾,也就是更频繁的被回收。
最老的世代保存了最成熟的对象,对这些对象的回收是最昂贵的,相当于一次完整的回收。可引起较长时间的停顿。解决方法是使用列车算法。
Garbage Collectors in the J2SE 5.0 HotSpot JVM
垃圾回收器的职责有1)分配内存;2)确保引用的内存在内存中;3)回收内存中不可达的对象。
制定垃圾回收算法的时候需要做出一些权衡和选择:1)串行还是并行;2)并发还是stop-the-world;3)内存紧致还是不紧致还是采用拷贝算法。
J2SE 5.0 HotSpot JVM中的四种GC采用的是世代收集器的原理,如下:
& & hotspot(j2se 5.0,按白皮书上的说法也适用于6.0)使用的是所谓的Generational Collection机制,也就是把对象分为young和old(还有一种是permanent),young对象经过几次回收后(存活较长时间后),就会成为old对象.之所以采用这种机制,是基于以下观察:大部分新建对象的引用不会持续太久,也就说是会少部分的引用会持续下来.
& & 因此,young generation 进行Collection会更多,因此使用的算法对时间效率的要求高.而old generation 保存的数据较多,使用的算法对空间的要求效率相对而言要求就较高了.把对象分为不同的generation,便于采用不同的算法进行操作.
对应的,可以把HotSpotJVM 的内存分为:young generation ,oldgeneration ,permanent generation.在这里默认首先把对像都放到区域的左边.
J2SEHotSpot JVM 的 4中垃圾回收器都是属于generational collection.
一般说来,新建对象时一般都是在young里面分配内存,old里面储存的是young中经过几次回收依然存活的对象以及一些一开始就在old中分配的大对象.而permanent generation里面保存的是类信息和元数据.
younggeneration分为一个Eden区域和两个survivor 区域.对象在Eden区域生成,经过一次GC后存活下来的对象进入survivor中,并在此经过更严格的考验,然后进入old .同一时间只有一个survivor区域保存对象,另一个为空.
当young区域满了后,就会执行young区域的GC算法.当old和permanent区域满了后,会先执行young的GC.再执行old和permanent的GC,如果old区域对象过多无法执行young的GC,那么在young区域执行old的GC算法(因为内存空间耗费较少),但是CMS回收器的old算法不行,下面会说明原因.
对于多线程的应用.JVM 使用一个Thread-LocalAllocation Buffers ,为每个线程分配一个区域来分配对象,以排除线程竞争.如果此区域满了的话就使用锁.
HotSpot的四种GC 回收器:
串行化回收serial collector:
特点:回收时会暂停应用.
young区域:将Eden和某个Survivor区域中的存活的对象复制到另一个Survivor区域(设为TO)(大对象直接放到old区域).如果TO区域满了,则直接复制到old区域.
old区域:使用mark-sweep-compact GC算法,也就是先标记存活对象,然后清除废弃对象,然后把存活对象都移到一块区域,空出一片较大的空闲空间.
适用范围:大部分客户端的应用都可以使用这种回收算法,这也是HotSpot默认的回收算法.在现在的机器(06年)上一个64MB的区域的一次完全回收所需的时间不到半秒钟.
并行回收parallel collector:
特点:可以利用多核的CPU.
young区域:同样还是要暂停应用,基本机制和串行化差不多,不过是使用多线程.可以加快效率.
old区域:同串行化.
多核计算机上面可以使用.
并行压缩回收parallel compacting collector:
与并行回收相比,主要是在old区域有个新的算法,同时,按白皮书的说法,这种回收最终会替代并行回收.
young区域:同并行回收.
old区域:首先,把old分为几个连续的区域.然后,在每个区域并行的进行检查,标记出alive的对象(先标记出可以直接引用的对象,然后是所有的).然后开始对这些区域进行检查,得出密集程度(左边的区域肯定比右边的密集),从某个密集程度不很高的区域开始,并行的对右边区域进行压缩.
适应范围:对于多核,且对pause time有要求的环境下,使用并行压缩回收比并行回收要好.但是对于高共享率的服务器(也就说一台服务器运行多个应用),由于old区域的collection较慢,又是多线程,所以一个应用的GC会对其他应用造成影响.对应的解决方法:可以配置减少并行时的线程数目.
并行标记清除回收Concurrent Mark Sweep collector:
young区域:同并行回收.
old区域:分为几个步骤.
Initialmark:在需要执行GC时,先暂停应用,然后把所有直接引用到的对象进行标记.
Concurrentmark:然后继续应用,并同时对已标记对象进行检查,得到所有存活的对象.
remark:再次暂停应用,对Concurrent mark持续期间应用程序修改了的对象进行检查(新增的,废弃的),并标记存活对象.这个阶段持续时间较长,因此会使用多线程.在阶段结束后,所有的存活对象都被标记了,未标记的对象就是垃圾对象了.
sweep:停止暂停应用程序,然后把所有垃圾对象的空间释放.
与其他算法的不同点:
第一:不执行压缩.不过会通过计算将来可能的内存需求而合并/分割某些内存块.
第二:不是old区域要满了才执行GC,而是在空间小于一定程度时开始.
第三:由于没执行压缩,因此会产生碎片.
另外,CMS还可以使用增量运行方式,就是在Concurrentmark阶段只执行一部分工作,然后把资源还给应用程序.回收器的工作会分为几个部分并安排在两次young区域的回收空闲阶段完成.这种模式一般用在对暂停时间有要求,同时处理器数目不多的情况下(单核或双核).
总体说来,与并行回收相比,CMS降低了old GC的暂停时间(有时候效果很显著),轻微的加长了young GC的时间(因为对象从young区域转到old区域时间会加长:没执行压缩,因此要先找到合适的区域),降低了整个系统的一些执行效率,以及很大的加强了对于内存空间的需求.
一些Java编程的建议
根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。一些关于程序设计的几点建议:
1.最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null.我们在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC的工作。
2.尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。
3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory.
4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。
5.当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。
j2se 5.0 hotspot 的四种垃圾回收器 -&
探秘Java垃圾回收机制 -&
Memory Management in the JavaHotSpotTM Virtual Machine -&
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1420902次
积分:19111
积分:19111
排名:第434名
原创:413篇
译文:29篇
评论:636条
文章:19篇
阅读:106982
(5)(4)(3)(4)(3)(1)(10)(4)(6)(5)(3)(3)(5)(2)(5)(5)(2)(2)(2)(3)(5)(4)(6)(1)(1)(3)(4)(10)(4)(9)(11)(10)(6)(8)(14)(4)(5)(14)(18)(12)(6)(12)(16)(19)(18)(27)(28)(3)(1)(10)(8)(1)(6)(4)(5)(19)(9)(8)(12)(1)(1)(2)(4)(2)(1)

我要回帖

更多关于 任务管理器没有进程 的文章

 

随机推荐