`

jvm之内存申请过程分析

阅读更多
----------~开篇分享一句话:【纸上得来终觉浅,绝知此事要躬行】~---------------------------------------
前置了解知识:http://wangxinchun.iteye.com/blog/2189321

内存申请过程
1、JVM会试图为相关Java对象在Eden中初始化一块内存区域;
2、当Eden空间足够时,内存申请结束。否则到下一步;
3、JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
4、Survivor区被用来作为Eden及old的中间交换区域,如果Survivor不足以放置eden区的对象,如果old区有空闲,那么直接放置在old区,Survivor区的对象会被移到Old区
5、当old区空间不够时,JVM会在old区进行major collection;
完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误";



1、jvm优先分配在eden区
2、当Eden空间足够时,内存申请结束。

证明:
jvm参数设置:
-Xmx20M -Xms20M -Xmn10M -verbose:gc  -XX:SurvivorRatio=8 -XX:+PrintGCDetails

其中:
-Xmx20M : 堆最大内存是20M
-Xms20M: 初始化也是20M
-Xmn10M :新生代10M,老年代10M
-verbose:gc  输出退出后的日志
-XX:SurvivorRatio=8:eden 为8M s0 s1 各1M
-XX:+PrintGCDetails:打印gc日志的详情

代码:
public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*2];
	}
}


日志输出:
Heap
 def new generation   total 9216K, used 2704K [0x03ac0000, 0x044c0000, 0x044c0000)
  eden space 8192K,  33% used [0x03ac0000, 0x03d64230, 0x042c0000)
  from space 1024K,   0% used [0x042c0000, 0x042c0000, 0x043c0000)
  to   space 1024K,   0% used [0x043c0000, 0x043c0000, 0x044c0000)
 tenured generation   total 10240K, used 0K [0x044c0000, 0x04ec0000, 0x04ec0000)
   the space 10240K,   0% used [0x044c0000, 0x044c0000, 0x044c0200, 0x04ec0000)
 compacting perm gen  total 12288K, used 2143K [0x04ec0000, 0x05ac0000, 0x08ec0000)
   the space 12288K,  17% used [0x04ec0000, 0x050d7fe0, 0x050d8000, 0x05ac0000)
No shared spaces configured.


通过分析main方法中申请2m的内存,内存分配到了eden区。from to tenered区都是没有被使用。



3、JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;

证明:
jvm参数设置同上
代码:
public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*9/10];
		byte[] b2 = new byte[1024*1024*8*9/10];
	}
}

日志:
[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 8560K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,  92% used [0x26ea0000, 0x275fc308, 0x276a0000)
  from space 1024K, 100% used [0x277a0000, 0x278a0000, 0x278a0000)
  to   space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
 tenured generation   total 10240K, used 38K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,   0% used [0x278a0000, 0x278a9920, 0x278a9a00, 0x282a0000)
 compacting perm gen  total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fba28, 0x282fbc00, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)


分析:
程序执行逻辑 先申请了1M的内存,然后再次申请8M的内存,考虑内存本身结构会占用内存空间为了避免边界都以9/10的比例来申请。
结果:
先分配了解决1M的内存在eden区,因为再次申请接近8M的内存时,eden区不够,发生了一次young gc,1M的内存分配到了from区,接近8M的内存放在了eden区。

[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
其中
9216K 为eden+from = 8M+1M=9M
19456K 为Xmx-to = 20M-1M = 19M
1250K->1024K :本次young gc 年轻代内存的变化
1250K->1062K :本次young gc 总堆内存的变化


4、Survivor区被用来作为Eden及old的中间交换区域,如果Survivor不足以放置eden区的对象,如果old区有空闲,那么直接放置在old区,Survivor区的对象会被移到Old区

证明:
jvm参数设置同上
程序如下:
public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*5];
		byte[] b2 = new byte[1024*1024*5];
		System.out.println(b1+""+b2);
	}
}

日志输出:
[GC [DefNew: 5448K->140K(9216K), 0.0051311 secs] 5448K->5260K(19456K), 0.0051644 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[B@c17164[B@1fb8ee3
Heap
 def new generation   total 9216K, used 5511K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,  65% used [0x26ea0000, 0x273deb58, 0x276a0000)
  from space 1024K,  13% used [0x277a0000, 0x277c32a8, 0x278a0000)
  to   space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
 tenured generation   total 10240K, used 5120K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,  50% used [0x278a0000, 0x27da0010, 0x27da0200, 0x282a0000)
 compacting perm gen  total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fbb28, 0x282fbc00, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)

分析:首先程序申请了5M的内存,放在了eden区,然后再次申请5M的内存,eden区不够,发生young gc,5M放置在from区,依然不够,故直接放置old 区
结果:eden区 8192*65/100/1024=5M
old区:10240*50/100/1024=5M

5、如果eden区不够,from区也承载不了,恰old区也已承载不了,那么会full gc
证明:
jvm参数配置同上
java代码:
public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*4];
		byte[] b2 = new byte[1024*1024*5];
		b1=null;
		b2=null;
		byte[] b3 = new byte[1024*1024*8];
	}
}

日志输出:
[GC [DefNew: 4424K->140K(9216K), 0.0056340 secs] 4424K->4236K(19456K), 0.0056837 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC [DefNew: 5260K->140K(9216K), 0.0008662 secs][Tenured: 4096K->140K(10240K), 0.0084034 secs] 9356K->140K(19456K), [Perm : 366K->366K(12288K)], 0.0093567 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 163K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,   2% used [0x26ea0000, 0x26ec8fc8, 0x276a0000)
  from space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
  to   space 1024K,   0% used [0x277a0000, 0x277a0000, 0x278a0000)
 tenured generation   total 10240K, used 8332K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,  81% used [0x278a0000, 0x280c3228, 0x280c3400, 0x282a0000)
 compacting perm gen  total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fba68, 0x282fbc00, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)


分析:
1、先申请了4M内存,放在eden区。
2、然后申请5M内存时,发生了young gc,无奈4M被安置在了old区,5M放在了eden区
3、再次向eden区申请8M的时候,eden区不够,同时8M还是放不下eden区,于是要full gc,full gc前先进行了young gc,5M 因为没有引用被清理,8M放在old区时,old区满,故full gc 4M也被清除old区。
结果:10240K,  81% used  为8M放在了old区


如果old区不够,会发生错误 java.lang.OutOfMemoryError: Java heap space

public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*4];
		byte[] b2 = new byte[1024*1024*5];
		b2=null;
		byte[] b3 = new byte[1024*1024*8];
	}
}

日志:
[GC [DefNew: 4424K->140K(9216K), 0.0057531 secs] 4424K->4236K(19456K), 0.0058056 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[Full GC [Tenured: 4236K->4233K(10240K), 0.0075081 secs] 4236K->4233K(19456K), [Perm : 366K->365K(12288K)], 0.0075750 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at test.test.TestEden.main(TestEden.java:8)
Heap
 def new generation   total 9216K, used 251K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,   3% used [0x26ea0000, 0x26edefd0, 0x276a0000)
  from space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
  to   space 1024K,   0% used [0x277a0000, 0x277a0000, 0x278a0000)
 tenured generation   total 10240K, used 4233K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,  41% used [0x278a0000, 0x27cc2630, 0x27cc2800, 0x282a0000)
 compacting perm gen  total 12288K, used 365K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fb688, 0x282fb800, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)


分析:
4M 先是放在eden区,5M申请时gc 4M转入old区,5M留在了eden区,8M申请时,发生gc5M在eden区被清理,因为eden区不够8M,所以分配到old区,old区发生full gc,full gc依然腾不出空间,报错。

注意:5M是在eden区gc时被清理,理由:[GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 日志显示(Tenured: 4096K->4236K(10240K))无变化,但是eden区和堆总内存变化(DefNew: 5260K->140K(9216K), 9356K->4236K(19456K));
1
0
分享到:
评论
1 楼 cumt168 2016-05-11  
写的很好
为什么初始化参数,年轻代-Xmn10M
def new generation   total 9216K, used 2704K [0x03ac0000, 0x044c0000, 0x044c0000)
而实际上显示只有9M呢?

相关推荐

    JVM内存空间分配笔记

    ### JVM内存空间分配详解 #### 一、JVM内存模型概览 JVM(Java虚拟机)内存模型主要由以下几个部分组成:程序计数器、Java虚拟机栈、本地方法栈、Java堆以及方法区(在JDK 8之后称为元空间)。下面将对这几个部分...

    jvm内存溢出

    **JVM内存溢出**是一种常见的运行时错误,指的是程序在执行过程中因为无法获得足够的内存资源而导致的问题。这种问题通常发生在程序尝试分配超出系统可用内存限制的新对象时。了解JVM内存结构是理解内存溢出的关键。...

    如何设置Tomcat的JVM虚拟机内存大小

    Tomcat JVM 虚拟机内存设置 在讨论 Tomcat JVM 虚拟机内存设置前,需要了解 Tomcat 的运行机制。Tomcat 本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个 Java 虚拟机。用户可以选择自己的操作...

    java基础之JVM

    2. **JVM内存结构**:主要包括堆、栈、方法区等,用于存储和管理数据。 3. **GC(Garbage Collection)算法**:自动内存管理机制,用于回收不再使用的对象所占用的内存空间。 4. **GC分析命令调优**:用于监控和优化...

    JVM内存有关知识点

    理解JVM内存管理对于优化代码性能、防止内存泄漏以及解决OutOfMemoryError等问题至关重要。以下是一些关于JVM内存的重要知识点: 1. **JVM内存结构**: JVM内存主要分为五个区域:方法区(Method Area)、堆(Heap...

    JVisualVM简介与内存泄漏实战分析

    ### JVM内存区域详解 了解JVM的内存布局有助于更好地理解内存泄漏等问题的发生原因。 - **Heap区**:堆是JVM管理的最大内存区域,用于存储所有Java对象实例。它可以进一步细分为: - **Eden Space**:新创建的...

    JVM内存模型及方法区

    "JVM内存模型及方法区" JVM内存模型是Java虚拟机(Java Virtual Machine,JVM)中的一种内存管理机制。它将内存区域分为五个部分:程序计数器、Java虚拟机栈、本地方法栈、Java堆和方法区。 1. 程序计数器...

    JVM与Hadoop介绍

    - **直接内存(Direct Memory)**:不在JVM内存区内,通过`java.nio.ByteBuffer.allocateDirect()`方式分配内存。 ##### 2. HotSpot运行时数据区域关系图 HotSpot虚拟机是Sun Microsystems开发的一款高性能的JVM实现...

    JVM基础知识及性能调优

    #### 二、JVM内存管理 - **堆空间**:分为年轻代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。 - **年轻代**:新创建的对象通常分配在此区域。 - **Eden区**:新对象首先被分配到...

    java内存泄露定位与分析[整理].pdf

    综上所述,Java内存泄露的定位与分析是一个涉及多方面知识的复杂过程,需要结合代码审查、性能监控和专业工具来找出问题并采取相应的优化措施。通过不断的学习和实践,开发者可以更好地理解和解决这类问题,确保系统...

    独立进程JVM设置.doc

    设定JVM内存分配时,遵循一个基本公式:服务所需内存(F)等于操作系统内存(O)加上所有Java进程JVM的最大值之和(A0+A1+A3...)。通常,应预留1-1.5GB的内存给操作系统使用。 在设置JVM大小时,需要注意几个要点...

    JVM调优话术

    JVM内存模型主要由以下几个部分组成: 1. **程序计数器(Program Counter Register)**:每条线程都有一个独立的程序计数器,用来记录当前线程正在执行的字节码指令的位置。当线程被中断或恢复时,可以通过程序计数器...

    Java内存分析技术在教学中的应用.pdf

    Java程序运行在Java虚拟机(JVM)中,与其他一些语言编写的程序直接向操作系统请求内存不同,Java程序运行时通过JVM向操作系统申请一定的内存,执行过程中所需的内存都由这块内存空间划分。 Java内存分析技术是指在...

    java内存分配 内存泄漏

    内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能看似无害,但随着时间推移,未释放的内存积累,可能导致程序运行缓慢或耗尽系统资源。Java中,内存泄漏通常是因为引用了不再使用的...

    tomcat内存泄漏备份方法

    - **合理配置JVM内存**:根据服务器硬件和应用需求设定合适的内存分配,避免因内存不足导致的频繁垃圾回收。 - **定期重启Tomcat**:定期重启服务有助于释放不再使用的内存和避免长时间运行的副作用。 - **优化...

    白鹤翔 JVM深入理解

    - 不属于Java堆的一部分,而是通过`NIO`库直接向操作系统申请的内存。 - 通常访问速度比Java堆更快,适合频繁读写的场景。 **2.5 Java栈** - 每个线程都有自己的Java栈,用于存储局部变量、方法参数等。 - Java栈在...

    创建string对象过程的内存分配:

    ### 创建string对象过程的内存分配详解 #### 一、引言 在Java中,`String` 类是最常用的数据类型之一,用于表示不可变的字符序列。`String` 对象的创建涉及复杂的内存分配机制,特别是在Java虚拟机 (JVM) 的环境中...

    JVM教程吐血整理干货.md

    2. **不受影响于OOM**:程序计数器是JVM内存区域中唯一不会发生内存溢出错误的部分。 3. **生命周期与线程相同**:程序计数器随线程创建而创建,随线程结束而销毁。 ##### Java虚拟机栈 Java虚拟机栈同样是线程...

    weblgicJVM内存不释放,CPU使用率居高不下建议.

    在AIX系统环境下,部署了WebLogic中间件的应用服务器(IP地址:75.16.17.8)出现了较为严重的问题:在运行过程中,该服务器上的WebLogic中间件JVM内存未能有效释放,导致JVM使用率高达90%以上;同时,服务器的CPU...

    内存溢出

    3. **JVM监控**:对于Java应用,JConsole、VisualVM等工具可以监控JVM的内存状态,包括堆内存、非堆内存和方法区等,帮助调整合适的JVM参数以防止内存溢出。 4. **日志分析**:程序运行日志中可能会包含内存异常信息...

Global site tag (gtag.js) - Google Analytics