垃圾回收机制算法分析

垃圾回收机制概述java

 Java语言中一个显著的特色就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候再也不须要考虑内存管理。因为有个垃圾回收机制,Java中的对象再也不有“做用域”的概念,只有对象的引用才有“做用域”。垃圾回收能够有效的防止内存泄露,有效的使用空闲的内存。c++

  ps:内存泄露是指该内存空间使用完毕以后未回收,在不涉及复杂数据结构的通常状况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序须要它的时间长度,咱们有时也将其称为“对象游离”。程序员

垃圾回收简要过程

  这里必须点出一个很重要的误区:不可达的对象并不会立刻就会被直接回收,而是至少要通过两次标记的过程。 
        第一次被标记过的对象,会检查该对象是否重写了finalize()方法。若是重写了该方法,则将其放入一个F-Query队列中,不然,直接将对象加入“即将回收”集合。在第二次标记以前,F-Query队列中的全部对象会逐个执行finalize()方法,可是不保证该队列中全部对象的finalize()方法都能被执行,这是由于JVM建立一个低优先级的线程去运行此队列中的方法,极可能在没有遍历完以前,就已经被剥夺了运行的权利。那么运行finalize()方法的意义何在呢?这是对象避免本身被清理的最后手段:若是在执行finalize()方法的过程当中,使得此对象从新与GC Roots引用链相连,则会在第二次标记过程当中将此对象从F-Query队列中清除,避免在此次回收中被清除,恢复成了一个“正常”的对象。但显然这种好事不能无限的发生,对于曾经执行过一次finalize()的对象来讲,以后若是再被标记,则不会再执行finalize()方法,只能等待被清除的命运。 
        以后,GC将对F-Queue中的对象进行第二次小规模的标记,将队列中从新与GC Roots引用链恢复链接的对象清除出“即将回收”集合。全部此集合中的内容将被回收。算法

手动GC回收

public class JVMDemo05 { public static void main(String[] args) { JVMDemo05 jvmDemo05 = new JVMDemo05(); //jvmDemo05 = null;
 System.gc(); } protected void finalize() throws Throwable { System.out.println("gc在回收对象..."); } }


finalize做用apache

Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,作必要的清理工做。这个方法是由垃圾收集器在肯定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,所以全部的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其余清理工做。finalize()方法是在垃圾收集器删除对象以前对这个对象调用的。浏览器

内存泄露

内存泄漏的定义:对象已经没有被应用程序使用,可是垃圾回收器没办法移除它们,由于还在被引用着。服务器

要想理解这个定义,咱们须要先了解一下对象在内存中的状态。下面的这张图就解释了什么是无用对象以及什么是未被引用对象。数据结构

 

上面图中能够看出,里面有被引用对象和未被引用对象。未被引用对象会被垃圾回收器回收,而被引用的对象却不会。未被引用的对象固然是再也不被使用的对象,由于没有对象再引用它。然而无用对象却不全是未被引用对象。其中还有被引用的。就是这种状况致使了内存泄漏。多线程

如何防止内存泄露

下面是几条容易上手的建议,来帮助你防止内存泄漏的发生。并发

  • 特别注意一些像HashMap、ArrayList的集合对象,它们常常会引起内存泄漏。当它们被声明为static时,它们的生命周期就会和应用程序同样长。
  • 特别注意事件监听和回调函数。当一个监听器在使用的时候被注册,但再也不使用以后却未被反注册。
  • “若是一个类本身管理内存,那开发人员就得当心内存泄漏问题了。” 一般一些成员变量引用其余对象,初始化的时候须要置空。

垃圾回收机制算法

引用计数法

1.1概述

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候刻计数器都为0的对象就是再也不被使用的,垃圾收集器将回收该对象使用的内存。

1.2优缺点

优势:

引用计数收集器能够很快的执行,交织在程序运行中。对程序须要不被长时间打断的实时环境比较有利。

缺点:

没法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0.并且每次加减很是浪费内存。

复制算法

S0和s1将可用内存按容量分红大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另外一块内存上去,而后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂状况,只须要移动堆顶指针,按顺序分配内存便可,实现简单,运行高效。

复制算法的缺点显而易见,可以使用的内存降为原来一半。

复制算法用于在新生代垃圾回收

 

标记清除算法

标记-清除(Mark-Sweep)算法顾名思义,主要就是两个动做,一个是标记,另外一个就是清除。

标记就是根据特定的算法(如:引用计数算法,可达性分析算法等)标出内存中哪些对象能够回收,哪些对象还要继续用。

标记指示回收,那就直接收掉;标记指示对象还能用,那就原地不动留下。

 

缺点

  1. 标记与清除没有连续性效率低;
  2. 清除以后内存会产生大量碎片;

因此碎片这个问题还得处理,怎么处理,看标记-整理算法。

标记-压缩算法

标记压缩法在标记清除基础之上作了优化,把存活的对象压缩到内存一端,然后进行垃圾清理。(java中老年代使用的就是标记压缩法)

分代收集算法

根据内存中对象的存活周期不一样,将内存划分为几块,java的虚拟机中通常把内存划分为新生代和年老代,当新建立对象时通常在新生代中分配内存空间,当新生代垃圾收集器回收几回以后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中没法找到足够的连续内存时也直接在年老代中建立。

于新生代和老年代来讲,生代回收频率很高,可是每次回收耗时很短,老年代回收频率较低,可是耗时会相对较长,因此应该尽可能减小老年代的GC.

 

为何老年代使用标记压缩、生代使用复制算法。

垃圾回收时的停顿现象

 

垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器能够更高效的执行,大部分状况下,会要求系统进如一个停顿的状态。停顿的目的是为了终止全部的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一个瞬间的一致性,也有利于更好的标记垃圾对象。所以在垃圾回收时,都会产生应用程序的停顿。

垃圾收集器

什么Java垃圾回收器

Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的自动分配(Memory Allocation)、自动回收(Garbage Collect)功能,这两个操做都发生在Java堆上(一段内存快)。某一个时点,一个对象若是有一个以上的引用(Rreference)指向它,那么该对象就为活着的(Live),不然死亡(Dead),视为垃圾,可被垃圾回收器回收再利用。垃圾回收操做须要消耗CPU、线程、时间等资源,因此容易理解的是垃圾回收操做不是实时的发生(对象死亡立刻释放),当内存消耗完或者是达到某一个指标(Threshold,使用内存占总内存的比列,好比0.75)时,触发垃圾回收操做。有一个对象死亡的例外,java.lang.Thread类型的对象即便没有引用,只要线程还在运行,就不会被回收。

串行回收器(Serial Collector)

单线程执行回收操做,回收期间暂停全部应用线程的执行,client模式下的默认回收器,经过-XX:+UseSerialGC命令行可选项强制指定。参数能够设置使用新生代串行和老年代串行回收器

年轻代的回收算法(Minor Collection)
Eden区的存活对象移到To区,To区装不下直接移到年老代,把From区的移到To区,To区装不下直接移到年老代,From区里面年龄很大的升级到年老代。 回收结束以后,Eden和From区都为空,此时把From和To的功能互换,From变To,To变From,每一轮回收以前To都是空的。设计的选型为复制。

年老代的回收算法(Full Collection)
年老代的回收分为三个步骤,标记(Mark)、清除(Sweep)、合并(Compact)。标记阶段把全部存活的对象标记出来,清除阶段释放全部死亡的对象,合并阶段 把全部活着的对象合并到年老代的前部分,把空闲的片断都留到后面。设计的选型为合并,减小内存的碎片。

并行回收

并行回收器(ParNew回收器)


并行回收器在串行回收器基础上作了改进,他可使用多个线程同时进行垃
圾回收,对于计算能力强的计算机而言,能够有效的缩短垃圾回收所需的尖
际时间。
ParNew回收器是一个工做在新生代的垃圾收集器,他只是简单的将串行回收
器多线程快他的回收策略和算法和串行回收器同样。
使用XX:+UseParNewGC 新生代ParNew回收器,老年代则使用市行回收器
ParNew回收器工做时的线程数量可使用XX:ParaleiGCThreads参数指
定,通常最好和计算机的CPU至关,避免过多的栽程影响性能。

并行回收集器(ParallelGC)

老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的
ParallelGC回收器同样,也是一种关往吞吐量的回收器,他使用了标记压缩
算法进行实现。
-XX:+UseParallelOldGC 进行设置
-XX:+ParallelCThread也能够设置垃圾收集时的线程教量。

 

CMS(并发GC)收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现的,整个收集过程大体分为4个步骤:

①.初始标记(CMS initial mark)

②.并发标记(CMS concurrenr mark)

③.从新标记(CMS remark)

④.并发清除(CMS concurrent sweep)

     其中初始标记、从新标记这两个步骤任然须要停顿其余用户线程。初始标记仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快,并发标记阶段是进行GC ROOTS 根搜索算法阶段,会断定对象是否存活。而从新标记阶段则是为了修正并发标记期间,因用户程序继续运行而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。

     因为整个过程当中耗时最长的并发标记和并发清除过程当中,收集器线程均可以与用户线程一块儿工做,因此总体来讲,CMS收集器的内存回收过程是与用户线程一块儿并发执行的。

CMS收集器的优势:并发收集、低停顿,可是CMS还远远达不到完美,器主要有三个显著缺点:

CMS收集器对CPU资源很是敏感。在并发阶段,虽然不会致使用户线程停顿,可是会占用CPU资源而致使引用程序变慢,总吞吐量降低。CMS默认启动的回收线程数是:(CPU数量+3) / 4。

CMS收集器没法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而致使另外一次Full  GC的产生。因为CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出如今标记过程以后,CMS没法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“浮动垃圾”。也是因为在垃圾收集阶段用户线程还须要运行,
即须要预留足够的内存空间给用户线程使用,所以CMS收集器不能像其余收集器那样等到老年代几乎彻底被填满了再进行收集,须要预留一部份内存空间提供并发收集时的程序运做使用。在默认设置下,CMS收集器在老年代使用了68%的空间时就会被激活,也能够经过参数-XX:CMSInitiatingOccupancyFraction的值来提供触发百分比,以下降内存回收次数提升性能。要是CMS运行期间预留的内存没法知足程序其余线程须要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来从新进行老年代的垃圾收集,这样停顿时间就很长了。因此说参数-XX:CMSInitiatingOccupancyFraction设置的太高将会很容易致使“Concurrent Mode Failure”失败,性能反而下降。

最后一个缺点,CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来不少麻烦,好比说大对象,内存空间找不到连续的空间来分配不得不提早触发一次Full  GC。为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关参数,用于在Full  GC以后增长一个碎片整理过程,还可经过-XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full  GC以后,跟着来一次碎片整理过程。

G1回收器

G1回收器(Garbage-First)实在]dk1.7中提出的垃圾回收器,从长期目标来看是为了取
CMS回收器,G1回收器拥有独特的垃圾回收策略,G1属于分代垃圾回收器,区分
新生代和老年代,依然有eden和from/to区,它并不要求整个eden区或者新生代、老
年代的空间都连续,它使用了分区算法。
并行性: G1回收期间可多线程同时工做。
井发性G1拥有与应用程序交替执行能力,部分工做可与应用程序同时执行,在整个
GC期间不会彻底阻塞应用程序。
分代GC:G1依然是一个分代的收集器,可是它是非两新生代和老年代一杯政的杂尊。
空间基理,G1在国收过程当中,不会微CMS那样在若千tacAy 要进行碎片整理。
G1
来用了有效复制对象的方式,减小空间碎片。
利得程,用于分区的缘由,G能够贝造取都分区城进行回收,帽小了国收的格想,
提高了性能。
使用.XXX:+UseG1GC 应用G1收集器,
Mills指定最大停顿时间
使用-XX:MaxGCPausel
设置并行回收的线程数量
使用-XX:ParallelGCThreads

Tomcat配置调优测试

Jmeter压力测试工具

JMeter是一款在国外很是流行和受欢迎的开源性能测试工具,像LoadRunner 同样,它也提供了一个利用本地Proxy Server(代理服务器)来录制生成测试脚本的功能,可是这个功能并很差用。因此在本文中介绍一个更为经常使用的方法——使用Badboy录制生成 JMeter 脚本。

简单的介绍一下BadboyBadboy是一款不错的Web自动化测试工具,若是你将它用于非商业用途,或者用于商业用途可是安装Badboy 的机器数量不超过5台,你是不须要为它支付任何费用的。也许是一种推广策略,Badboy提供了将Web测试脚本直接导出生成JMeter 脚本的功能,而且这个功能很是好用,也很是简单。你能够跟着下面的试验步骤来迈出你在开源世界的第一步。

1.      经过Badboy的官方网站下载Badboy的最新版本;

2.      安装Badboy。安装过程同通常的Windows 应用程序没有什么区别,安装完成后你能够在桌面和Windows开始菜单中看到相应的快捷方式——若是找不到,能够找一下Badboy安装目录下的Badboy.exe 文件,直接双击启动Badboy

3.      启动Badboy,你能够看到下面的界面。

 

在地址栏(图中红色方框标注的部分)中输入你须要录制的Web应用的URL——这里咱们以http://www.yahoo.com 为例,并点击GO 按钮开始录制。若是你用过LoadRunner之类的商业工具,对于这个操做必定不会陌生吧 ^_^

4.      开始录制后,你能够直接在Badboy内嵌的浏览器(主界面的右侧)中对被测应用进行操做,全部的操做都会被记录在主界面左侧的编辑窗口中——在这个试验中,咱们在Yahoo的搜索引擎中输入 JMeter 进行搜索。不过你将看到,录制下来的脚本并非一行行的代码,而是一个个Web对象——这就有点像LoadRunner的VuGen中的Tree View视图;

5.      录制完成后,点击工具栏中的“中止录制”按钮,完成脚本的录制;

6.      选择“File -> Export to JMeter”菜单,填写文件名“login_mantis.jmx”,将录制好脚本导出为JMeter脚本格式。也能够选择“File -> Save”菜单保存为Badboy脚本;

7.      启动JMeter并打开刚刚生成的测试脚本。

也许你已经急不可待的准备开始尝试着用JMeter处理你手头的工做了^_^ 在下面的几节,我将继续为你们介绍如何在 JMeter 中完成一个测试场景的设置和JMeter测试结果分析入门,以及如何参数化JMeter脚本。

固然,若是你的动手能力很强,几分钟你就能够熟悉这些内容。不过仍是请容许我一点点由浅入深的来帮你们完成JMeter从入门到精通”的过程。我相信在这个过程当中你将会了解到更多有关性能测试的知识和经验,甚至包括一些LoadRunner等商业测试工具所没法提供给你的经验。

 

测试串行吞吐量

-XX:+PrintGCDetails -Xmx32M -Xms32M

-XX:+HeapDumpOnOutOfMemoryError

-XX:+UseSerialGC

-XX:PermSize=32M

项目启动GC回收6次 吞吐量390

扩大堆的内存

-XX:+PrintGCDetails -Xmx512M –Xms532M

-XX:+HeapDumpOnOutOfMemoryError

-XX:+UseSerialGC

-XX:PermSize=32M

GC回收6次  445

结论 最大内存越大,吞吐量越高。

调整初始

-XX:+PrintGCDetails -Xmx512M –Xms512M

-XX:+HeapDumpOnOutOfMemoryError

-XX:+UseSerialGC

-XX:PermSize=32M

GC回收0次  492

 

并行回收UseParNewGC

-XX:+PrintGCDetails -Xmx512M –Xms512M

-XX:+HeapDumpOnOutOfMemoryError

-XX:+UseParNewGC

-XX:PermSize=32M

GC回收0次 吞吐量452

 

 

 

并行合并回收UseParallelGC

-XX:+PrintGCDetails -Xmx512M -Xms256M

-XX:+HeapDumpOnOutOfMemoryError

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:ParallelGCThreads=8

-XX:PermSize=32M

GC回收0次 吞吐量

调优总结

初始堆值和最大内存内存越大,吞吐量就越高。

最好使用并行收集器,由于并行手机器速度比串行吞吐量高,速度快。

设置堆内存新生的比例和老年代的比例最好1:2或者1:3。

减小GC对老年代的回收。

相关文章
相关标签/搜索