`
liuzhaodong89
  • 浏览: 60369 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

jvm对大对象分配内存的特殊处理

    博客分类:
  • jvm
阅读更多

    前段日子在和leader交流技术的时候,偶然听到jvm在分配内存空间给大对象时,如果young区空间不足会直接在old区切一块过去。对于这个结论很好奇,也比较怀疑,所以就上网搜了下,发现还真有这么回事。以下给出具体代码来说明:

首先定义好jvm内存各个区域的大小。我设定的是eden区8M,from和to各1M,old区10M,总共20M的空间,参数如下:

-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

    紧接着,开始写程序。很简单,就是初始化一个9M的程序,然后用jstat命令看jdk的内存使用情况。

public class App {
	private static final int _1MB = 1024 * 1024;

	/**
	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
	 * -XX:PretenureSizeThreshold=3145728
	 */
	public static void main(String[] args) {
		byte[] allocation = new byte[9*_1MB];
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

    然后打成jar,执行。结果如下:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  18.04  90.00  23.08      0    0.000    20    0.027    0.027

    果然,当对象大小大于eden区的时候会直接扔到old区。但我还不满足与此,于是将对象改大了些,改成了11M。再次尝试发现结果如下:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.taobao.jdkmem.App.main(App.java:17)

    到这里结束了么?当然没有:)这个是一个大的完整的对象,当大对象本身是由一连串的小对象组成的时候,会不会不再OOM呢?于是改了代码再次尝试:

public class App {
	private static final int _1MB = 1024 * 1024;

	/**
	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
	 * -XX:PretenureSizeThreshold=3145728
	 */
	public static void main(String[] args) {
		byte[][] allocation;
		allocation = new byte[11][_1MB]; // 直接分配在老年代中
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

    再次运行,结果如下:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00  38.06  67.68  60.02  23.10      1    0.007    14    0.012    0.019

    果然,这次居然又被jvm给生吃下去了。不过这次并非所有的都在old区,而是有一部分还在young区里活着。看来jvm还是足够彪悍的。

    由此可见,当出现大对象的时候,jvm用牺牲部分宝贵的old区的方式来保证了整个jvm的正常运转。所以,程序中尽量要避免大对象,如果实在不行,就让大对象活的尽量久些,莫要new一个然后gc掉再new一个再gc,这么爆jvm可不太友好。

    到这里结束了吧?你猜对了,还没有:P既然知道jvm会对大对象申请内存做特殊处理,那么就在琢磨程序员有没有方法干预这个过程呢?答案是有的,就是使用这个参数-XX:PretenureSizeThreshold。这个参数的单位是Byte,其作用是当新对象申请的内存空间大于这个参数值的时候,直接扔到old区。做个试验就证明了:

public class App {
	private static final int _1MB = 1024 * 1024;

	/**
	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
	 * -XX:PretenureSizeThreshold=3145728
	 */
	public static void main(String[] args) {
		byte[] allocation = new byte[4*_1MB];
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

    运行命令如下:

java -jar -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728 memtest-1.0-SNAPSHOT.jar 

    我设置的阈值是3M,意味着超过3M的对象会被直接扔到old区。结果是皆大欢喜,新对象直接被扔到了old区:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  18.04  40.00  23.08      0    0.000     0    0.000    0.000
    试验有了结果,自然而然心情愉悦。但这个参数使用时需要慎重,因为fullgc的代价很高,因此old区就显得非常宝贵。除非你真的清楚你在干什么,否则莫要轻易玩这个参数,万一搞个频繁fullgc就玩大了。ok,到此打完收工。

 

 

9
13
分享到:
评论
11 楼 wenxin45445 2017-03-13  
最近也在学习jvm,对上面说的一条规则是大对象直接进入老年代,测试了一下,jdk6和7好像还不太相同,请问一下,这个大对象究竟多大算大对象?6和7有区别吗?
10 楼 337240552 2012-09-10  
想不通怎么这么多人踩呢。。。。期待踩的人写出更好的
9 楼 liuzhaodong89 2012-08-31  
bhdweb 写道
博主请问是怎么看到这些的

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT  
  0.00   0.00  18.04  90.00  23.08      0    0.000    20    0.027    0.027

我也想去试试。。可不知道怎么看

用jstat命令,这个是jvm自带的命令,可以到网上google一下用法的
8 楼 lection.yu 2012-08-31  
高人。jvm了解到深入许多。不过我是从来遇到过一次创建这么大对象的场景
7 楼 bhdweb 2012-08-31  
博主请问是怎么看到这些的

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT  
  0.00   0.00  18.04  90.00  23.08      0    0.000    20    0.027    0.027

我也想去试试。。可不知道怎么看
6 楼 songbin0201 2012-08-31  
嗯,学习了,一次不错的尝试和总结过程
5 楼 w156445045 2012-08-31  
看不懂写的啥~
4 楼 magicyang919 2012-08-31  
牛xx人啊
3 楼 337240552 2012-08-31  
2 楼 saturn 2012-08-31  
1 楼 7454103 2012-08-31  
nice 

相关推荐

    JVM内存分配与垃圾回收详解

    4. Java 堆:它是 Java 虚拟机管理的内存中最大的一块,是被所有线程共享的一块内存区域,它在虚拟机启动时创建,它的唯一目的是存储对象实例,几乎所有的对象都在这里分配内存。Java 堆也是垃圾收集器管理的主要...

    Sun JVM原理与内存管理

    - 特殊情况下直接在 Old 区分配,如大对象或长时间存活的对象。 - TLAB (Thread Local Allocation Buffer) 是一种优化技术,允许每个线程拥有自己的缓冲区,从而加快分配速度。 2. **栈上分配**: - 适用于原子...

    MAT JVM 内存分析工具.

    5. ** dominator tree**:这是一种特殊的引用树,显示了哪些对象控制着内存的大部分。根节点是全局最不可达的对象,即没有其他对象引用它的对象,而从根到叶的路径代表了内存消耗的路径。 6. **对象集分析**:MAT...

    内存分配源代码MemoryAllocation.rar

    当执行`new`操作时,JVM会在堆中为对象分配空间,并调用构造函数初始化对象。而栈内存则用于存储方法调用时的局部变量,一旦方法执行完毕,这些局部变量就会被自动释放。 "MemoryAllocation.java"这个源码文件可能...

    JVM中[直接内存]的所有内容-pdf

    在JVM的内存模型中,直接内存(Direct Memory)是一个特殊的区域,虽然它并不属于标准的JVM运行时数据区,但对高性能的应用程序设计有着重要的影响。本文将深入探讨直接内存的概念、用途、优缺点以及如何进行配置。 ...

    jdk,jvm源码

    5. 内存管理:JVM的垃圾回收机制自动管理内存,包括对象的分配和回收。常见的垃圾收集算法有标记-清除、复制、标记-整理和分代收集等。 6. 多线程:JVM支持多线程并发执行,每个线程都有自己的程序计数器和虚拟机栈...

    JVM笔记(阳哥).zip

    准备为类的静态变量分配内存并初始化为默认值;解析将符号引用转换为直接引用;初始化执行类的初始化方法。了解这个过程有助于我们理解和控制类的生命周期。 四、垃圾收集与内存优化 JVM的垃圾收集机制负责自动...

    深入虚拟机---JVM调优总结(摘自网上网上大牛分享).pdf

    新一代的垃圾回收算法,如Garbage First(G1)收集器,旨在更好地处理大内存应用的性能问题。G1收集器将堆内存划分为多个区域,跟踪各区域垃圾回收的价值和成本,并优先收集价值最高的区域。 在调优过程中,我们...

    JVM.pdf

    3. **准备**:为类的静态变量分配内存并赋予默认初始值。非静态变量的内存分配发生在对象实例化时。 4. **解析**:将符号引用转换为直接引用,不一定要在初始化之前完成。 5. **初始化**:执行类中定义的初始化...

    JVM体系架构

    直接内存是 JVM 体系架构中的一种特殊的内存区域,它并不是虚拟机内存的一部分,也不是 Java 虚拟机规范中定义的内存区域。直接内存是通过 NIO 库中的 DirectBuffer 类来分配的,它可以调用 Native 方法直接分配堆外...

    resin-jvm 调优

    gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。 在充分理解了...

    JVM TI Watch dll

    JVM TI允许开发人员创建工具,如内存分析器、性能监视器、调试器等,这些工具可以深入洞察JVM的内部工作,包括类加载、对象分配、线程活动、方法执行等。 在标题"JVM TI Watch dll"中,"Watch dll"通常指的是一个...

    深入JVM笔记word版

    Java虚拟机(JVM)作为Java程序的运行环境,负责管理和分配内存资源。为了更好地理解和掌握JVM内部的工作机制,本篇文章将重点介绍JVM中的几个关键内存区域:程序计数器、Java虚拟机栈、本地方法栈、堆以及方法区。 #...

    JAVA虚拟机内存分配机制

    堆内存则是JVM用来动态分配对象和数组的地方。当使用`new`关键字创建一个对象时,对象的数据成员和方法将被存储在堆内存中。由于堆内存的分配和回收由Java的垃圾回收机制自动管理,所以开发者无需手动释放内存。然而...

    JVM 38道面试题及答案.docx

    Java 内存分配寄存器:Java 内存分配寄存器是 JVM 中的一块特殊区域,用于存储对象的引用变量和基本类型变量的值。 静态域:静态域是指 static 定义的静态成员,它们的生命周期和虚拟机相同。 常量池:常量池是...

    java中的栈(深层了解java虚拟机对对象的内存分布)

    与栈相比,堆的最大优点在于能够动态分配内存,其大小和生存周期无需事先告知编译器,由Java的垃圾回收机制自动管理。这种灵活性使得堆成为复杂数据结构和大型对象的理想选择。然而,动态内存分配的代价是存取速度...

    JVM 77 道面试题及答案.docx

    内存的一部分用于创建堆空间,当程序中创建对象时,就从对空间中分配内存。GC是JVM内部的一个进程,回收无效对象的内存用于将来的分配。 10. JVM内存区域: JVM内存区域主要分为线程私有区域【程序计数器、虚拟机栈...

    JVM性能优化相关问题.pdf

    在加载阶段,JVM通过类的全限定名获取类的二进制数据,并在方法区为这个类分配内存,同时在堆中创建一个代表这个类的java.lang.Class对象。验证阶段是对类文件的格式、元数据、字节码和符号引用等进行检验,确保类...

    Java虚拟机内存管理总结

    声明一个对象rect时,将在栈内存为对象的引用变量rect分配内存空间,但Rectangle的值为空,称rect是一个空对象。空对象不能使用,因为它还没有引用任何"实体"。(2)对象实例化时的内存模型当执行rect=new Rectangle...

Global site tag (gtag.js) - Google Analytics