JVM GC中Stop the world案例实战

原创 2016年08月31日 00:00:34

GC中Stop the world案例实战

为了更好的理解GC中的Stop the world案例,就必须先了解何为Stop the World方式。所谓的Stop the World机制,简称STW,即在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起(具体运行机制见图4-1)。此时,系统只能允许GC线程进行运行,其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行。这些工作都是由虚拟机在后台自动发起和自动完成的,是在用户不可见的情况下把用户正常工作的线程全部停下来,这对于很多的应用程序,尤其是那些对于实时性要求很高的程序来说是难以接受的。但是有些时候对于虚拟机来说采用Stop the world机制是无法避免的,例如采用复制算法时,为了保证在复制存活的对象的时候,对象的一致性,不然要使应用程序被挂起。但是随着java虚拟机的发展,HotSpot虚拟机团队为达到更好用户体验而一直进行着努力,不断的对垃圾收集器进行着改进,随着JDK的版本的不断更新,更好的垃圾收集器的出现,用户线程的停顿时间也在不断缩短,虽然这一时间现阶段仍然不能消除,但相信不久的未来一定会有更好的垃圾收集器被发现,从而完全达到用户对于虚拟机垃圾回收的性能要求。


图4-1Stop the World机制的GC

         对于很多的垃圾收集器来说,都会采用Stop the World机制来进行垃圾回收。具体来讲,在Java虚拟机的Serial, ParNew, Parallel Scanvange, ParallelOld, Serial Old全程都会Stop the world,JVM这时候只运行GC线程,不运行用户线程。而CMS主要分为 initial Mark, Concurrent Mark, ReMark,Concurrent Sweep等阶段,initial Mark和Remark占整体的时间比较较小,它们会Stop the world. Concurrent Mark和Concurrent Sweep会和用户线程一起运行。虽然CMS减少了stop the world的次数,不可避免地让整体GC的时间拉长了。各个垃圾收集器GC采用的方式见图4-2。

                                               

图4-2各个垃圾收集器GC采用的方式

 

了解了Stop the world机制,接下来将展示一个案例来讲解GC中Stop the World机制具体的运行机制,让读者有一个更直观的印象。

/**
*案例4.1 测试的SWT机制的GC
**/
package ErrorType;
import java.util.ArrayList;
import java.util.List;
/**
*
**/
class Person{ 
	private String name;
	private String age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAge() {
		return age;
	}
	public void setAge(String age) {
		this.age = age;
	}
}

public class Test {
	public static void main(String[] args) {
		List<Person> persons = new ArrayList<Person>();
		long start = 0L;
		long end = 0L;
		long runtime = 0L;
		int count = 0;
		while(true) {
			start = System.currentTimeMillis();
			persons.add(new Person());
			count++;
			end = System.currentTimeMillis();
			runtime = end - start;
			System.out.println(count + " : Use time in one operation:" + runtime + "ms");
			start = end;
		}
	}

}

Eclipse参数设置:

-Xms100M -Xmx100M -Xss128k -XX:+PrintGCDetails 
-XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:./gclogs

参数解释:

-Xms和-Xmx分别设置了堆的初始大小为10M和最大大小也为10M,这两个参数一般配合使用,且设置为同一值。-Xss参数设定了每个线程堆栈大小为128k  -XX:+PrintGCDetails 表示打印GC的信息,这里通过-Xloggc:./gclogs将GC信息输出到gclogs文件中。-XX:+PrintGCDateStamps则表示在GC信息里显示当前时间。-XX:+PrintHeapAtGC表示在每次GC前显示当前堆中的状态。

这里截取部分输出:

38503 : Use time in one operation:0ms
38504 : Use time in one operation:0ms
38505 : Use time in one operation:1ms
38506 : Use time in one operation:0ms
38507 : Use time in one operation:0ms
38508 : Use time in one operation:0ms
38509 : Use time in one operation:0ms

可以看出,正常进行一次persons.add(newPerson())操作耗时是不到1ms的,但是在将第38505个对象进行persons.add(new Person())操作时,耗时将达到1ms,这说明这时系统进行了GC,且使用了STW机制。

另一方面,从GC日志文件gclogs中,可以看到如下内容:

Java HotSpot(TM) 64-Bit Server VM (25.73-b02) for windows-amd64 JRE (1.8.0_73-b02), built on Jan 29 2016 17:38:49 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 12034908k(8867180k free), swap 13345628k(8226180k free)
CommandLine flags: -XX:InitialHeapSize=104857600 -XX:MaxHeapSize=104857600 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:SurvivorRatio=1 -XX:ThreadStackSize=128 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 22528K, used 11264K [0x00000000fdf00000, 0x0000000100000000, 0x0000000100000000)
  eden space 11264K, 100% used [0x00000000fdf00000,0x00000000fea00000,0x00000000fea00000)
  from space 11264K, 0% used [0x00000000ff500000,0x00000000ff500000,0x0000000100000000)
  to   space 11264K, 0% used [0x00000000fea00000,0x00000000fea00000,0x00000000ff500000)
 ParOldGen       total 68608K, used 0K [0x00000000f9c00000, 0x00000000fdf00000, 0x00000000fdf00000)
  object space 68608K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fdf00000)
 Metaspace       used 2750K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 301K, capacity 386K, committed 512K, reserved 1048576K
2016-05-15T12:59:45.962+0800: 0.988: [GC (Allocation Failure) [PSYoungGen: 11264K->1226K(22528K)] 11264K->1234K(91136K), 0.0047652 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

可以看到在GC前,新生代eden区域使用已经达到100%,从而导致新对象的内存分配失败引发了Minor GC,日志里显示了PSYoungGen、ParOldGen以及Metaspace等区域的使用情况,这里不做详细解释。

下面,主要讲解下最后的GC输出信息。

         YGC发生于2016-05-15T12:59:45.962+0800,YGC前新生代占用大小为11264K,约为12M,YGC后新生代占用内存大小为1226K,约为1M,而新生代总大小为22528K,约为22M。YGC前JVM堆内存占用为11264K,YGC后堆内存占用为1234K,JVM对总大小为91136K,约91M。期间用户态耗时小于0.01s,内核态耗时小于0.01s,实际耗时为0.01s。

         同样JVM中给开发者提供了一个参数,-XX:+PrintGCApplicationStoppedTime,来显示应用程序在Java虚拟机进行所有GC暂停的总耗时。

         到这里,STW的例子就讲完了,相信对着对GC过程又有了新一层的认识了。


版权声明:本文为博主原创文章,转载时请标明出处。

Java垃圾回收中Stop-The-World和JVM中的Stop-The-World

Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停...
  • b_11111
  • b_11111
  • 2016年10月02日 22:34
  • 2971

GC日志中的stop-the-world

通常使用下面的JVM选项在GC日志中打印”stop-the-world”(STW)暂停时间。 -XX:+PrintGCApplicationStoppedTime       但是在GC日志中看,会...
  • zero__007
  • zero__007
  • 2016年12月23日 17:29
  • 1638

JAVA垃圾收集器之ParNew收集器

1、特点 ParNew收集器是JAVA虚拟机中垃圾收集器的一种。它是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:...
  • ffm83
  • ffm83
  • 2015年01月19日 14:07
  • 6543

JVM GC中Stop the world案例实战

GC中Stop the world案例实战
  • sinat_25306771
  • sinat_25306771
  • 2016年08月31日 00:00
  • 3045

[Java JVM] Hotspot GC研究- GC安全点 (Safepoint&Stop The World)

什么是safepoint引用openjdk官网的一段话: A point during program execution at which all GC roots are known and ...
  • lqp276
  • lqp276
  • 2016年08月19日 11:56
  • 1598

基于日志理解 cms 原理,为什么remark要stop the world?(理解CMS GC日志.)

理解CMS GC日志 本文翻译自: https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs 加入自己的思考,特别是为什么rem...
  • fei33423
  • fei33423
  • 2017年04月29日 00:04
  • 639

JVM进阶(八)——Stop The World

JVM进阶(八)——Stop The World  小伙伴还记得上一篇中我们留下的一个问题吗?什么是停顿类型!经过前几章的学习,我们知道垃圾回收首先是要经过标记的。对象被标记后就会根据不同的区域采用不...
  • sunhuaqiang1
  • sunhuaqiang1
  • 2017年01月21日 17:30
  • 42872

用空间换时间 —— Java虚拟机的算法实现

HotSpot虚拟机是如何实现可达性分析算法的?
  • hzy38324
  • hzy38324
  • 2017年08月13日 16:36
  • 1745

深入Go语言网络库的基础实现

Go语言的出现,让我见到了一门语言把网络编程这件事情给做“正确”了,当然,除了Go语言以外,还有很多语言也把这件事情做”正确”了。我一直坚持着这样的理念——要做"正确"的事情,而不是"高性能"的事情;...
  • xiaolei1982
  • xiaolei1982
  • 2016年12月10日 23:37
  • 999

JVM 性能调优实战之:一次系统性能瓶颈的寻找过程

玩过性能优化的朋友都清楚,性能优化的关键并不在于怎么进行优化,而在于怎么找到当前系统的性能瓶颈。性能优化分为好几个层次,比如系统层次、算法层次、代码层次...JVM 的性能优化被认为是底层优化,门槛较...
  • defonds
  • defonds
  • 2016年09月20日 17:39
  • 23801
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JVM GC中Stop the world案例实战
举报原因:
原因补充:

(最多只允许输入30个字)