要了解Java垃圾收集机制,先理解JVM内存模式是很是重要的。今天咱们将会了解JVM内存的各个部分、如何监控以及垃圾收集调优。html
正如你从上面的图片看到的,JVM内存被分红多个独立的部分。普遍地说,JVM堆内存被分为两部分——年轻代(Young Generation)和老年代(Old Generation)。java
年轻代是全部新对象产生的地方。当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫作Minor GC。年轻代被分为3个部分——Enden区和两个Survivor区。算法
年轻代空间的要点:shell
年老代内存里包含了长期存活的对象和通过屡次Minor GC后依然存活下来的对象。一般会在老年代内存被占满时进行垃圾回收。老年代的垃圾收集叫作Major GC。Major GC会花费更多的时间。服务器
全部的垃圾收集都是“Stop the World”事件,由于全部的应用线程都会停下来直到操做完成(因此叫“Stop the World”)。多线程
由于年轻代里的对象都是一些临时(short-lived )对象,执行Minor GC很是快,因此应用不会受到(“Stop the World”)影响。并发
因为Major GC会检查全部存活的对象,所以会花费更长的时间。应该尽可能减小Major GC。由于Major GC会在垃圾回收期间让你的应用反应迟钝,因此若是你有一个须要快速响应的应用发生屡次Major GC,你会看到超时错误。oracle
垃圾回收时间取决于垃圾回收策略。这就是为何有必要去监控垃圾收集和对垃圾收集进行调优。从而避免要求快速响应的应用出现超时错误。jsp
永久代或者“Perm Gen”包含了JVM须要的应用元数据,这些元数据描述了在应用里使用的类和方法。注意,永久代不是Java堆内存的一部分。ide
永久代存放JVM运行时使用的类。永久代一样包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。
方法区是永久代空间的一部分,并用来存储类型信息(运行时常量和静态变量)和方法代码和构造函数代码。
若是JVM实现支持,JVM内存管理会为建立内存池,用来为不变对象建立对象池。字符串池就是内存池类型的一个很好的例子。内存池能够属于堆或者永久代,这取决于JVM内存管理的实现。
运行时常量池是每一个类常量池的运行时表明。它包含了类的运行时常量和静态方法。运行时常量池是方法区的一部分。
Java栈内存用于运行线程。它们包含了方法里的临时数据、堆里其它对象引用的特定数据。你能够阅读栈内存和堆内存的区别。
Java提供了大量的内存开关(参数),咱们能够用它来设置内存大小和它们的比例。下面是一些经常使用的开关:
VM 开关 | VM 开关描述 |
---|---|
-Xms | 设置JVM启动时堆的初始化大小。 |
-Xmx | 设置堆最大值。 |
-Xmn | 设置年轻代的空间大小,剩下的为老年代的空间大小。 |
-XX:PermGen | 设置永久代内存的初始化大小。 |
-XX:MaxPermGen | 设置永久代的最大值。 |
-XX:SurvivorRatio | 提供Eden区和survivor区的空间比例。好比,若是年轻代的大小为10m而且VM开关是-XX:SurvivorRatio=2,那么将会保留5m内存给Eden区和每一个Survivor区分配2.5m内存。默认比例是8。 |
-XX:NewRatio | 提供年老代和年轻代的比例大小。默认值是2。 |
大多数时候,上面的选项已经足够使用了。可是若是你还想了解其余的选项,那么请查看JVM选项官方网页。
Java垃圾回收会找出没用的对象,把它从内存中移除并释放出内存给之后建立的对象使用。Java程序语言中的一个最大优势是自动垃圾回收,不像其余的程序语言那样须要手动分配和释放内存,好比C语言。
垃圾收集器是一个后台运行程序。它管理着内存中的全部对象并找出没被引用的对象。全部的这些未引用的对象都会被删除,回收它们的空间并分配给其余对象。
一个基本的垃圾回收过程涉及三个步骤:
简单标记和清除方法存在两个问题:
上面简单清除方法的问题在于Java垃圾收集的分代回收的,并且在堆内存里有年轻代和年老代两个区域。我已经在上面解释了Minor GC和Major GC是怎样扫描对象,以及如何把对象从一个分代空间移到另一个分代空间。
这里有五种能够在应用里使用的垃圾回收类型。仅须要使用JVM开关就能够在咱们的应用里启用垃圾回收策略。让咱们一块儿来逐一了解:
咱们可使用命令行和图形工具来监控监控应用垃圾回收。例如,我使用Java SE下载页中的一个demo来实验。
若是你想使用一样的应用,能够到Java SE下载页面下载JDK 7和JavaFX演示和示例。我使用的示例应用是Java2Demo.jar,它位于 jdk1.7.0_55/demo/jfc/Java2D 目录下。这只是一个可选步骤,你能够运行GC监控命令监控任何Java应用。
我打开演示应用使用的命令是:
1
|
pankaj@Pankaj:~
/Downloads/jdk1
.7.0_55
/demo/jfc/Java2D
$ java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar
|
可使用jstat命令行工具监控JVM内存和垃圾回收。标准的JDK已经附带了jstat,因此不须要作任何额外的事情就能够获得它。
要运行jstat你须要知道应用的进程id,你可使用 ps -eaf | grep java 命令获取进程id。
1
2
3
|
pankaj@Pankaj:~$
ps
-eaf |
grep
Java2Demo.jar
501 9582 11579 0 9:48PM ttys000 0:21.66
/usr/bin/java
-Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar
501 14073 14045 0 9:48PM ttys002 0:00.00
grep
Java2Demo.jar
|
从上面知道,个人Java应用进程id是9582。如今能够运行jstat命令了,就像下面展现的同样:
1
2
3
4
5
6
7
8
9
|
pankaj@Pankaj:~$ jstat -gc 9582 1000
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 0.0 8192.0 7933.3 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8026.5 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8030.0 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8122.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 0.0 0.0 8192.0 8171.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
1024.0 1024.0 48.7 0.0 8192.0 106.7 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656
1024.0 1024.0 48.7 0.0 8192.0 145.8 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656
|
jstat命令的最后一个参数是每一个输出的时间间隔。每隔一秒就会打印出内存和垃圾收集数据。
让咱们一块儿来对每一列的意义进行逐一了解:
jstat的优势,咱们一样能够在没有GUI的远程服务器上运行jstat。注意:咱们是经过 -Xmn10m 选项来指定S0C、S1C和EC的总和为10m的。
若是你想在GUI里查看内存和GC,那么可使用jvisualvm工具。Java VisualVM一样是JDK的一部分,因此你不须要单独去下载。
在终端运行jvisualvm命令启动Java VisualVM程序。一旦启动程序,你须要从Tools->Plugins选项安装Visual GC插件,就像下面图片展现的。
安装完Visual GC插件后,从左边栏打开应用并把视角转到Visual GC部分。你将会获得关于JVM内存和垃圾收集详情,以下图所示。
Java垃圾回收调优应该是提高应用吞吐量的最后一个选择。在你发现应用因为长时间垃圾回收致使了应用性能降低、出现超时的时候,应该考虑Java垃圾收集调优。
若是你在日志里看到 java.lang.OutOfMemoryError: PermGen space错误,那么能够尝试使用 -XX:PermGen 和 -XX:MaxPermGen JVM选项去监控并增长Perm Gen内存空间。你也能够尝试使用-XX:+CMSClassUnloadingEnabled并查看使用CMS垃圾收集器的执行性能。
若是你看到了大量的Full GC操做,那么你应该尝试增大老年代的内存空间。
全面垃圾收集调优要花费大量的努力和时间,这里没有一尘不变的硬性调优规则。你须要去尝试不一样的选项而且对这些选项进行对比,从而找出最适合本身应用的方案。
这就是全部的Java内存模型和垃圾回收内容。但愿对你理解JVM内存和垃圾收集过程有所帮助。