Java虚拟机内存模型及垃圾回收监控调优
Java虚拟机内存模型及垃圾回收监控调优
如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要。今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优。
JVM内存模型
正如你上图所看到的,JVM内存可以划分为不同的部分,广义上,JVM堆内存可以划分为两部分:年轻代和老年代(Young Generation and Old Generation)
年轻代(Young Generation)
年轻代用于存放由new所生成的对象。当年轻代空间满时,垃圾回收就会执行。这个垃圾回收我们称之为 最小化垃圾回收(Minor GC)。年轻代又可以分为三分部: 一个Eden内存区(Eden Memory)和两个Survivor 内存区(Survivor Memory)。
关于年轻代的重点:
- 大多数新创建的对象都存放在Eden内存区。
- 当Eden区存满对象时,最小化GC将会被执行,所有存活的对象被移到其中一个Survivor区。
- 最小化GC同时也会检查存活的对象并将它们移到另一个Survivor区,所以在一段时间,其中一个Survivor区总是空的。
- 存活的对象经过多次GC后,将会被移到老年代(Old Generation)通常年轻代转化多长时间变为老年代都会设置一个阀值。
老年代(Old Generation)
老年代内存包含那些经过多次最小化GC且存活的对象。 通常当老年代内存满时垃圾回收会被执行。我们称之为大GC (Major GC),通常这个过程会花费很长的时间。
Stop the World Event
所有的垃圾回收都是“阻塞”事件(“Stop the World” events) ,因为所有应用程序线程必须停止,直到垃圾回收操作完成之后才能继续。
由于年轻代总是保存着短期存活的对象,最小化GC非常快,应用程序也不会受到影响。然而大GC(Major GC)由于它要检查所有存活的对象,所以需要花费很长的时间。 大GC会在垃圾回收期间使得你的应用程序没有任何响应,应最大化的减少此类GC。如果你有一个即时响应应用程序且总是有很多的大GC在执行。你会发现存在超时错误。
垃圾回器扫行的时间依赖于GC所使用的策略。这就是为什么对于那些高响应用程序来说,GC的监控与调优显得非常必要。
永久代(Permanent Generation)
永久代或持久代包含JVM用来描述应用程序中使用的类和方法的元数据。注意 永久代不是JAVA堆内存的一部分。
JVM在运行期间依据应用程序所使用的类来存入永久代,同时也包含Java SE库类各方法。 永久代中的对象通过全GC(Full GC)来进行垃圾回收。
方法区Method Area
方法区是永久代的一部分,用于存储类结构(运行时的常量和静态变量)及方法和构造的代码。
运行时常量池Runtime Constant Pool
运行时常量池是类中常量池在编译期的表示,它包含类的运行时常量,静态方法,它是方法区的一部分。
Java栈内存Java Stack Memory
Java 栈内存用于执行线程。包含方法短期存活的特定值及方法中所涉及的指向堆中其它对象的引用。
Java 堆内存Switches(Java Heap Memory Switches)
提供了大量的内存Switch,我们可以用来设置内存大小及它们的比率。一此常用的内存Switches如下。
VM Switch | VM Switch 描述 |
---|---|
-Xms | 设置JVM启动时堆初始值。 |
-Xmx | 设置堆的最大值 |
-Xmn | 设置年轻代的大小 |
-XX:PermGen | 设置永久代内存的初始值 |
-XX:MaxPermGen | 设置永久代内存的最大值 |
-XX:SurvivorRatio | 用于提供Eden区和Survivor区的比例, 例如,如果年轻代的大小为 10M,VM switch is -XX:SurvivorRatio=2 那么 5 M 分配给 Eden 区,每一个Survivor 区为 2.5 M .默认比例大小为 8。 |
-XX:NewRatio | 老年代/新生代的比例. 默认值是 2. |
大多数情况下,上述选项是非常有用的。如果你也想试试其它参数可以查看 JVM官方主页
JAVA GC
Java GC是从内存中标记、移除非可用对象并释放所分配的空间用于后续创建对像的过程。Java编程语言最大的功能之一就是自动垃圾回收。不像其它类似C的编程语言,它们的内存分配与回收可以人为操作。
GC是后台执行用于检查内存中的所有对象并找出未被任务程序所引用的对象。所有这些未引用的对象将被删除,同时空间也会被回收用于分配给其它对象。
关于标记、删除方法有两个问题:
1.由于新创建的对象将会变成不可用,所有效率上不是很高。
2.多次GC后可用的对象很能在后续的GC后仍然可用。
上述方法的不足在于Java的GC是分代的,在堆内存中分年轻代和老年代。 前面我已经解释过基于最小化GC(Minor GC)和大GC(Major GC)对象是如果被扫描并从一个分代区移到别一个分区。
Java GC的类型
在应用程序中我们可以使用如下五种类型的GC。我们可以使用JVM相关设置为应用程序开启GC策略,现在我们就来具体的看看.
- 串行GC (Serial GC -XX:+UseSerialGC): 串行GC为年轻代和老年代GC提供简单的标记-清除-压缩方法。串行GC在独立应用程序运行的少量CPU的客户机器上非常有效。对于那些低内存占用的应用程序非常有利。
- 并行GC (Parallel GC -XX:+UseParallelGC): 并行GC中大部分实现与串行GC相同,除了年轻代的垃圾回收使用N个线程,N是系统CPU内核的数量。我们可以通过 JVM 的 -XX:ParallelGCThreads=n 选项来控制线程的数量。并行的垃圾回收器因为使用多CPU来加速GC的实现所以也称为并行收集器(throughput collector).并行GC使用单线程来处理老年代的垃圾回收.
- 并行Old GC (Parallel Old GC -XX:+UseParallelOldGC):此类型的GC中年轻代和老年代GC都使用多线程进行。其它与并行GC相同,
- 并发标记清除收集器(Concurrent Mark Sweep (CMS) Collector) (-XX:+UseConcMarkSweepGC): CMS收集器是并发短暂停收集器。它为老年代进行垃圾回收。CMS收集器 通过与应用程序线程并发执行垃圾回收 从而缩短因GC而出现的暂停时间。CMS收集器在年轻代使用与并行收集器相同的算法。这类垃圾收集器适合不能容忍长时间暂停的响应程序。我们可以通过JVM中的 -XX:ParallelCMSThreads=n 来设置CMS收集器中的线程数。
- G1 垃圾收集器G1 Garbage Collector (-XX:+UseG1GC) Java 7中可以使用G1 垃圾收集器。它的最终目标是替换掉CMS收集器。G1收集器是一个并行、并发、增量压缩、低暂停的垃圾收集器。
G1垃圾收集器不同于其它收集器,它没有年轻代、老年代空间,它将堆空间划分成多个相同大小的堆区域(heap regions),当调用一次垃圾回收,它首先收集实时性不是很高的区域的数据,因此称为”Garbage First”。有关详细的可参考我的另外一篇Blog:Java垃圾收集器之--Garbage-First Collector 。
JAVA 垃圾收集监控
我们可以使用Java命令行和UI工具来监控应用程序的垃圾收集活动。下面的例子中,我使用Java SE Downloads 中一个演示程序。
如果你想使用同样的程序,前往 Java SE Downloads 页面下载JDK 7 and JavaFX Demos and Samples。 我使用的和序示例是Java2Demo.jar 可以在jdk1.7.0_55/demo/jfc/Java2D 目录中找到。当然了这步可选。你可以选用任何Java程序执行GC监控命令。
我使用的启动演示程序的命令是:
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
我们可以使用 jstat 命令行工具来监控JVM内存和GC活动。标准的JDK中含有此命令。因此可以直接使用。
运行 jstat 之前你需要知道程序的进程ID号。你可以运行 ps -eaf | grep java 命令来获取。
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 命令。
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命令的最后一个参数是每次输出的时间间隔,因此它会每隔一秒打印内存及垃圾回收数据。下面详细看看每列。
- S0C and S1C: 这列显示当前 Survivor0 and Survivor1 区域的大小(KB)。
- S0U and S1U: 这列显示 Survivor0 and Survivor1 区域的使用情况(KB). 其中一个Survivor区域总是空的。
- EC and EU: 这列显示Eden区的当前大小及使用情况(KB). 注意 EU 的大小逐渐增加,当达到EC大小, 最小化 GC 被调用 ,EU 的大小减小。
- OC and OU: 这列显示了老年代的当前大小及使用情况(KB)。
- PC and PU: 这列显示了 持久化代(Perm Gen) 的当前大小及使用情况(KB)。
- YGC and YGCT: YGC 列显示年轻代发生GC事件的数量。 YGCT 列显示年轻代发生GC操作累计时间. 注意这两列值都在增加与EU值减少是在同一行。这主要是最小化GC的原因。
- FGC and FGCT: FGC 列显示了FUll GC发生的数量. FGCT 列显示了FULL GC操作的累计时间. 注意Full GC 所用的时间相比年轻代GC所用的时间要大的多。
- GCT: 这列显示了GC 操作总累计时间。 注意它是 YGCT 和 FGCT 值的总和。
jstat的优点在于它可以在无GUI的远程服务器上执行。注意S0C, S1C 和EC之和为 10M ,与我们通过JVM选项 -Xmn10m 设置的值一样。
Java VisualVM with Visual GC
如果你想在GUI下查看内存及GC操作。 那么你可以使用 jvisualvm 工具。Java VisualVM 也同样包含在JDK中,你不需要单独下载。
只需要运行在终端上执行jvisualvm 命令来启动Java VisualVM程序。一旦启动,你需要通过Tools--》Plugins选项安装Visual GC 插件,正如下图所示。
Visual GC 安装完毕后,左边列中打开程序前往Visual GC 部分。你将会得到如下图所示的JVM内存及GC截图。
Java GC 调优是提高应用程序吞吐量的最后选择,只有当你发现长时间的GC导致性能下降而产生应用程序超时。
你会在日志中看到java.lang.OutOfMemoryError: PermGen space的错误信息。然后可以尝试监控并通过使用JVM 选项 -XX:PermGen and -XX:MaxPermGen 来增加Perm Gen内存空间。你或许也可以尝试使用-XX:+CMSClassUnloadingEnabled 来检查使用CMS垃圾收集的性能如何?
如果你发现大量的FUll GC操作,你可以试着增加老年代内存空间。
总之GC调优需要花费大量的精力和时间,这里绝没有什么硬性或者快速的规则。你需要尝试不同的选项,比较他们,并找对你应用程序来说最好的那个。
这就是所有有关Java内存模型和垃圾回收。我希望这有助于你理解JVM内存模型及垃圾回收的过程。谢谢。
原文链接: JournalDev 翻译: TonySpark
译文链接: http://www.cnblogs.com/tonyspark/p/3731696.html
[ 转载请保留原文出处、译者和译文链接。]
Java虚拟机内存模型及垃圾回收监控调优的更多相关文章
- python内存机制与垃圾回收、调优手段
目录 一.python的内存机制 二.python的垃圾回收 1. 引用计数 1.1 原理: 1.2 优缺点: 1.4 两种情况: 2. 标记清除 2.1 原理: 2.2 优缺点: 3. 分代回收 3 ...
- 探秘Java虚拟机——内存管理与垃圾回收
本文主要是基于Sun JDK 1.6 Garbage Collector(作者:毕玄)的整理与总结,原文请读者在网上搜索. 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始 ...
- 探秘Java虚拟机——内存管理与垃圾回收(转)
本文主要是基于Sun JDK 1.6 Garbage Collector(作者:毕玄)的整理与总结,原文请读者在网上搜索. 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始 ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
- 程序猿的日常——JVM内存模型与垃圾回收
Java开发有个很基础的问题,虽然我们平时接触的不多,但是了解它却成为Java开发的必备基础--这就是JVM.在C++中我们需要手动申请内存然后释放内存,否则就会出现对象已经不再使用内存却仍被占用的情 ...
- JVM内存模型和垃圾回收
Java开发有个很基础的问题,虽然我们平时接触的不多,但是了解它却成为Java开发的必备基础——这就是JVM.在C++中我们需要手动申请内存然后释放内存,否则就会出现对象已经不再使用内存却仍被占用的情 ...
- 【Java_基础】JVM内存模型与垃圾回收机制
1. JVM内存模型 Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间. JVM内存模型如下图所示 1.1 程序计数器 程序计数器( ...
- (转载)JVM中的内存模型与垃圾回收
转载自微信公众号:Java高级架构(Java-jiagou)-----看完这篇文章,我奶奶都知道JVM中的内存模型与垃圾回收了! 六.内存模型 6.1 内存模型与运行时数据区 Java虚拟机在执行J ...
- Java虚拟机--内存模型与线程
Java虚拟机--内存模型与线程 高速缓存:处理器要与内存交互,如读取.存储运算结果,而计算机的存储设备和处理器的运算速度差异巨大,所以加入一层读写速度和处理器接近的高速缓存来作为内存和处理器之间的缓 ...
随机推荐
- Redis集群方案介绍
由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...
- 查询SQLServer 服务器,执行过的SQL语句耗时!
SELECT creation_time N'语句编译时间',last_execution_time N'上次执行时间',total_physical_reads N'物理读取总次数',total_l ...
- SVN分支的合并和同步
使用svn几年了,一直对分支和合并敬而远之,一来是因为分支的管理不该我操心,二来即使涉及到分支的管理,也不敢贸然使用合并功能,生怕合并出了问题对团队造成不良影响,最主要的原因是,自己对分支的目的和合并 ...
- 一. JVM发展史,运行时数据区域,四大引用
一.JVM的出现 JVM将字节码解释成不同os下的机器指令,有了jvm,java语言在不同平台上运行时不需要重新编译 虚拟机发展史 (1)Sun Classic classic jvm要么采用纯解释器 ...
- 6. redis持久化,认证
一. RDB 快照持久化 (默认) rdb机制,在满足redis.conf中配置的持久化策略后, 自动持久化数据, 默认存储在dump.rdb文件中 策略 : save 900 1 : 900秒内,一 ...
- Spark on yarn配置项说明与优化整理
配置于spark-default.conf 1. #spark.yarn.applicationMaster.waitTries 5 用于applicationMaster等待Spark maste ...
- Spark 1.4连接mysql诡异的问题及解决
在spark-default.conf文件中明明配置了mysql的数据源连接 随后启动spark-shell 执行如下测试代码: import org.apache.spark.{SparkConte ...
- Laravel中URL,ACTION,ROUTE区别
创建路由如下所示: Route::get('articles',['uses'=>'ArticlesController@index','as'=>'articles.index']); ...
- 查看Oracle的SID用户名等信息
源地址:http://zhidao.baidu.com/question/681563406501077052.html 用sysdba身份登录 比如 conn /as sysdba 匿名管理员登陆 ...
- C# 连接操作MYSQL 数据库_02
using MySql.Data.MySqlclient; // 引进MYSQL 库 // C# 连接 MYSQL 数据库更好的方法 void func_02() { string str_conn_ ...