在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

JVM内存堆布局图解分析

2016-12-13 12:56| 发布者: zhangjf| 查看: 588| 评论: 0

摘要: JAVA能够实现跨平台的一个根本原因,是定义了class文件的格式标准,凡是实现该标准的JVM都能够加载并解释该class文件,据此也可以知道,为啥java语言的执行速度比C/C++语言执行的速度要慢了,当然原因肯定不止这一个, ...

JAVA能够实现跨平台的一个根本原因,是定义了class文件的格式标准,凡是实现该标准的JVM都能够加载并解释该class文件,据此也可以知道,为啥java语言的执行速度比C/C++语言执行的速度要慢了,当然原因肯定不止这一个,如在JVM中没有数据寄存器,指令集使用的是栈来保存中间数据...等,尽管Java的贡献者们为执行速度的提高想了各种办法,如JIT、动态编译器等,以下是Leetcode中一道题目用不同的语言实现时的执行性能对比图...

 JVM内存堆布局图解分析

以下是JVM的一个基本架构图,在这个基本架构图中,栈有两部份,Java线程栈以及本地方法栈,栈的概念与C/C++程序基本上都是一个概念,里面存放的都是栈帧,一个栈帧代表的就是一个函数的调用,在栈帧里面存放了函数的形参,函数的局部变量, 返回地址等,但是与C/C++的 一个重要区别是,C/C++里面有传值以及传址的区别,当传的是一个对象时( 结构体也可以当成对象,其实就是对象~,只不过里面的方法默认都是public的,不信你可以试试,在结构体中加一个函数,编译器也不会报错,程序依旧运 行~~~),会将对象复到到栈中,而Java中只有基本类型才是传值的,其他类型传的都是引用,什么是引用,学过C/C++的就把引用当作指针理解 吧~~~,在这个基本架构图中,可以看出JVM还定义了一个本地方法栈,本地方法栈是为Java调用本地方法【这些本地方法是由其他语言编写的】服务的

 JVM内存堆布局图解分析

上面的图中看到的是JVM中栈有两个,但是堆只有一个,每一个线程都有自已的线程栈【线程栈的大小可以通过设置JVM的-xss参数进行配置,32位系统下,一般默认的大小是512K】,线程栈里面的数据属于该线程私有,但是所有的线程都共享一个堆空间,堆中存放的是对象数据,什么是对象数据,排除法,排除基本类型以及引用类型以外的数据都将放在堆空间中,下面来具体分析一下堆空间...

在JVM中堆空间划分如下图所示

 JVM内存堆布局图解分析

上图中,刻画了java程序运行时的堆空间,可以简述成如下2条

1.JVM中堆空间可以分成三个大区,新生代、老年代、永久代

2.新生代可以划分为三个区,Eden区,两个幸存区

在JVM运行时,可以通过配置以下参数改变整个JVM堆的配置比例

  1. 1.JVM运行时堆的大小
  2.   -Xms堆的最小值
  3.   -Xmx堆空间的最大值
  4. 2.新生代堆空间大小调整
  5.   -XX:NewSize新生代的最小值
  6.   -XX:MaxNewSize新生代的最大值
  7.   -XX:NewRatio设置新生代与老年代在堆空间的大小
  8.   -XX:SurvivorRatio新生代中Eden所占区域的大小
  9. 3.永久代大小调整
  10.   -XX:MaxPermSize
  11. 4.其他
  12.   -XX:MaxTenuringThreshold,设置将新生代对象转到老年代时需要经过多少次垃圾回收,但是仍然没有被回收
复制代码

在上面的配置中,老年代所占空间的大小是由-XX:SurvivorRatio这个参数进行配置的,看完了上面的JVM堆空间分配图,可能会奇怪,为啥新生代空间要划分为三个区Eden及两个Survivor区?有何用意?为什么要这么分?要理解这个问题,就得理解一下JVM的垃圾收集机制(复制算法也叫copy算法),步骤如下:

复制(Copying)算法 将内存平均分成A、B两块,算法过程: 1. 新生对象被分配到A块中未使用的内存当中。当A块的内存用完了, 把A块的存活对象对象复制到B块。 2. 清理A块所有对象。 3. 新生对象被分配的B块中未使用的内存当中。当B块的内存用完了, 把B块的存活对象对象复制到A块。 4. 清理B块所有对象。 5. goto 1。 优点:简单高效。缺点:内存代价高,有效内存为占用内存的一半。 图解说明如下所示:(图中后观是一个循环过程)  JVM内存堆布局图解分析 对复制算法进一步优化:使用Eden/S0/S1三个分区 平均分成A/B块太浪费内存,采用Eden/S0/S1三个区更合理,空间比例为Eden:S0:S1==8:1:1,有效内存(即可分配新生对象的内存)是总内存的9/10。 算法过程: 1. Eden+S0可分配新生对象; 2. 对Eden+S0进行垃圾收集,存活对象复制到S1。清理Eden+S0。一次新生代GC结束。 3. Eden+S1可分配新生对象; 4. 对Eden+S1进行垃圾收集,存活对象复制到S0。清理Eden+S1。二次新生代GC结束。 5. goto 1。 默认Eden:S0:S1=8:1:1,因此,新生代中可以使用的内存空间大小占用新生代的9/10,那么有人就会问,为什么不直接分成两个区,一个区占9/10,另一个区占1/10,这样做的原因大概有以下几种 1.S0与S1的区间明显较小,有效新生代空间为Eden+S0/S1,因此有效空间就大,增加了内存使用率 2.有利于对象代的计算,当一个对象在S0/S1中达到设置的XX:MaxTenuringThreshold 值后,会将其分到老年代中,设想一下,如果没有S0/S1,直接分成两个区,该如何计算对象经过了多少次GC还没被释放,你可能会说,在对象里加一个计数 器记录经过的GC次数,或者存在一张映射表记录对象和GC次数的关系,是的,可以,但是这样的话,会扫描整个新生代中的对象, 有了S0/S1我们就可以只扫描S0/S1区了~~~ GC实例分析,下次再分析~

来自:http://www.cnblogs.com/WJ5888/p/4374791.html

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-9 01:12

Copyright 2015-2025 djqfx

返回顶部