JVM调优系列(一)——JVM模型架构图解析
2016-06-03 18:02
288 查看
JVM模型架构图
一、方法区
Method Area 方法区也是JVM内存模型中的重要内存区域,主要用于存放常量和类的定义信息。与Heap类似,也是被JVM中的线程共享。
常见异常:当方法区无法满足内存分配需求时,将抛出OutOfMemeryError异常
二、java 堆
Heap,java运行时内存中最重要的部分,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,分为新生代和老年代两部分,分别用于存放刚刚产生的对象和年轻的对象;如果对象一直没被回收,则被移入老年代。
1、新生代又进一步细分为 eden、survivor(s0,s1)
1)Eden
伊甸园,对象的出生地,大部分刚刚创建的对象会被放在Eden区。
2)Survivor
幸存区,存放那些经历过至少一次回收幸存下来的对象。如果幸存区中的对象到了指定年龄仍未被回收,则有机会进入老年代 Tenured。
注:堆是线程共享的
2、常见异常
如果堆中没有内存完成实例分配,并且堆也无法再拓展时,将会抛出OutOfMemeryError异常
三、java 虚拟机栈
JVM language Stacks,也是线程的私有内存空间,和java线程在同一时间创建,主要保存方法的局部变量、部分结果等数据。
1、栈帧
每个方法在执行的同时都会创建一个栈帧(用于该方法的局部变量表、部分结果等信息的数据结构;方法的参数,局部变量越多,那么栈帧的局部变量表就会越大,栈帧会膨胀扩大内存以满足方法调用所需的参数传递,单个方法调用所需的栈空间也会随之增大。)栈帧的基本结构如下,每个区域都有各自的职责,用于存储方法的特定数据。
因为编码中最长涉及的就是局部变量表空间使用,所以这里着重分析栈中的局部变量表应用。
1.1、局部变量表
局部变量表(Local Variable Table)用于存放方法参数和方法内部定义的局部变量.以变量槽(Variable Slott)为最小单位. 一个Slot可以存放一个32位的数据类型,Java中占用32位以内的数据类型有boolean、byte、char、short、int、float;,对于 64位的数据类型((long
double)),虚拟机会以高位对齐的方式为其分配两个连续的Slot空间进行存储。
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。Java栈也是线程私有的。
2、对于栈有两种异常情况
1)StackOverflowError
如果线程请求的栈深度大于栈可用深度,将抛出StackOverflowError异常;
2)OutOfMemoryError
如果虚拟机栈可以动态拓展,在拓展的时无法申请到足够的内存,将会抛出OutOfMemoryError异常
3、虚拟栈大小对程序的影响
在HotSpot 虚拟机中,可使用-Xss参数来设置虚拟栈的大小,这个值直接决定了函数调用可达的深度。
1)无参数、少成员变量
栈大小设置为16m: -Xss16m stack最大深度:142954
栈大小设置为1m :-Xss1m stack 最大深度:5838
2)当方法带有多个参数和局部变量时:
-Xss16m stack 深度:116627
-Xss1m stack 深度:4354
这个结果相对测试1无局部变量、参数的情况,方法递归次数变少。这是因为方法的参数,局部变量越多,那么栈帧的局部变量表就会越大,栈帧会膨胀扩大内存以满足方法调用所需的参数传递,单个方法调用所需的栈空间也会随之增大。随着函数调用参数和局部变量的增加,单次函数调用对栈空间的需求也会增加,固定栈空间大小的情况下,可调用次数必然减少
4、局部变量空间重用
局部变量表的空间是可以重用的。
方法2中同样定义了a、b两个变量,但他们的作用范围相同,不存在重用的可能。
编码不当导致栈空间无法释放又未被重用——栈溢出问题
如果一个变量被保存到局部变量表中,则系统垃圾回收时,就无法回收这部分空间。例如定义一个局部变量b,作用于方法体内,在GC调用时,变量b已经超过了它的作用范围(这里可以理解为已经完成了变量的使命,理应被回收变量所占空间),但最后GC却无法回收b变量所占空间,b变量仍在局部变量表中。
假设在变量失效后,在这个函数体内,又未能定义足够多的局部变量来复用该变量的表空间,则整个函数体内这块内存区域是不会被回收的。如果函数体执行非常费时同时又申请了较大的内存空间无法释放,则对系统性能将会造成较大压力。
解决办法
手动设置null值,帮助GC回收
合理的定义变量,灰常重要!!
四、程序计数器
Program Count Register,每一个线程都有一个独立的程序计数器,用于记录下一条要运行的指令,各线程间PC Register 互不影响。
程序计数器是线程的私有内存空间,每条线程都会拥有一个独立的程序计数器
五、本地方法栈
Native Method Stacks与java栈非常相似,它们之间的区别不过是Java栈执行Java方法,本地方法栈执行的是本地方法。 本地方法是由C实现的,和Java栈一样,本地方法栈也是线程私有的,也可能抛出StackOverflowError和OutOfMemeryError异常,在HotSpot虚拟机中,对本地方法栈和虚拟机栈不做区分。
六、总结
一、方法区
Method Area 方法区也是JVM内存模型中的重要内存区域,主要用于存放常量和类的定义信息。与Heap类似,也是被JVM中的线程共享。
常见异常:当方法区无法满足内存分配需求时,将抛出OutOfMemeryError异常
二、java 堆
Heap,java运行时内存中最重要的部分,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,分为新生代和老年代两部分,分别用于存放刚刚产生的对象和年轻的对象;如果对象一直没被回收,则被移入老年代。
1、新生代又进一步细分为 eden、survivor(s0,s1)
1)Eden
伊甸园,对象的出生地,大部分刚刚创建的对象会被放在Eden区。
2)Survivor
幸存区,存放那些经历过至少一次回收幸存下来的对象。如果幸存区中的对象到了指定年龄仍未被回收,则有机会进入老年代 Tenured。
注:堆是线程共享的
2、常见异常
如果堆中没有内存完成实例分配,并且堆也无法再拓展时,将会抛出OutOfMemeryError异常
三、java 虚拟机栈
JVM language Stacks,也是线程的私有内存空间,和java线程在同一时间创建,主要保存方法的局部变量、部分结果等数据。
1、栈帧
每个方法在执行的同时都会创建一个栈帧(用于该方法的局部变量表、部分结果等信息的数据结构;方法的参数,局部变量越多,那么栈帧的局部变量表就会越大,栈帧会膨胀扩大内存以满足方法调用所需的参数传递,单个方法调用所需的栈空间也会随之增大。)栈帧的基本结构如下,每个区域都有各自的职责,用于存储方法的特定数据。
因为编码中最长涉及的就是局部变量表空间使用,所以这里着重分析栈中的局部变量表应用。
1.1、局部变量表
局部变量表(Local Variable Table)用于存放方法参数和方法内部定义的局部变量.以变量槽(Variable Slott)为最小单位. 一个Slot可以存放一个32位的数据类型,Java中占用32位以内的数据类型有boolean、byte、char、short、int、float;,对于 64位的数据类型((long
double)),虚拟机会以高位对齐的方式为其分配两个连续的Slot空间进行存储。
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。Java栈也是线程私有的。
2、对于栈有两种异常情况
1)StackOverflowError
如果线程请求的栈深度大于栈可用深度,将抛出StackOverflowError异常;
2)OutOfMemoryError
如果虚拟机栈可以动态拓展,在拓展的时无法申请到足够的内存,将会抛出OutOfMemoryError异常
3、虚拟栈大小对程序的影响
在HotSpot 虚拟机中,可使用-Xss参数来设置虚拟栈的大小,这个值直接决定了函数调用可达的深度。
1)无参数、少成员变量
public class StackTest { private int count=0; public void recount() { count++; System.out.println("stack 深度:"+count); //System.out.println(Runtime.getRuntime().maxMemory()); //最大可用内存,对应-Xmx recount(); } @Test public void test() { try { recount(); } catch (Exception e) { e.printStackTrace(); } }上面代码为一个递归调用,使用count记录递归的层次,修改JVM栈大小,当栈溢出时,执行结果如下:
栈大小设置为16m: -Xss16m stack最大深度:142954
栈大小设置为1m :-Xss1m stack 最大深度:5838
2)当方法带有多个参数和局部变量时:
public class StackTest2 { private int count=0; public void recount(long a,long b,long c) { long d =0; long e =0; long f =0; //占用栈空间 count++; System.out.println("stack 深度:"+count); recount(a,b,c); } @Test public void test() { try { recount(1L,2L,3L); } catch (Exception e) { e.printStackTrace(); } }
-Xss16m stack 深度:116627
-Xss1m stack 深度:4354
这个结果相对测试1无局部变量、参数的情况,方法递归次数变少。这是因为方法的参数,局部变量越多,那么栈帧的局部变量表就会越大,栈帧会膨胀扩大内存以满足方法调用所需的参数传递,单个方法调用所需的栈空间也会随之增大。随着函数调用参数和局部变量的增加,单次函数调用对栈空间的需求也会增加,固定栈空间大小的情况下,可调用次数必然减少
4、局部变量空间重用
局部变量表的空间是可以重用的。
public void memory() { long a = 0; } long b = 0; public void memory2() { long a = 0; long b = 0; }方法1中变量a 的作用域仅限于方法体内,故在定义变量b时,a已经没有意义,故b可以重用a所在的空间。
方法2中同样定义了a、b两个变量,但他们的作用范围相同,不存在重用的可能。
编码不当导致栈空间无法释放又未被重用——栈溢出问题
如果一个变量被保存到局部变量表中,则系统垃圾回收时,就无法回收这部分空间。例如定义一个局部变量b,作用于方法体内,在GC调用时,变量b已经超过了它的作用范围(这里可以理解为已经完成了变量的使命,理应被回收变量所占空间),但最后GC却无法回收b变量所占空间,b变量仍在局部变量表中。
假设在变量失效后,在这个函数体内,又未能定义足够多的局部变量来复用该变量的表空间,则整个函数体内这块内存区域是不会被回收的。如果函数体执行非常费时同时又申请了较大的内存空间无法释放,则对系统性能将会造成较大压力。
解决办法
手动设置null值,帮助GC回收
合理的定义变量,灰常重要!!
四、程序计数器
Program Count Register,每一个线程都有一个独立的程序计数器,用于记录下一条要运行的指令,各线程间PC Register 互不影响。
程序计数器是线程的私有内存空间,每条线程都会拥有一个独立的程序计数器
五、本地方法栈
Native Method Stacks与java栈非常相似,它们之间的区别不过是Java栈执行Java方法,本地方法栈执行的是本地方法。 本地方法是由C实现的,和Java栈一样,本地方法栈也是线程私有的,也可能抛出StackOverflowError和OutOfMemeryError异常,在HotSpot虚拟机中,对本地方法栈和虚拟机栈不做区分。
六、总结
名称 | 作用 | 范围 | 常见异常 |
程序计数器 | 记录当前线程的下一条命令 | 私有 | |
虚拟机栈 | 存放方法的参数、局部变量 | 私有 | StackOverFlowError OutOfMemoryError |
本地方法栈 | 存放本地方法的参数、局部变量 | 私有 | 同上 |
堆 | 存放对象的实例 | 共享 | OutOfMemoryError |
方法区 | 存放常量、类定义信息 | 共享 | OutOfMemoryError |
相关文章推荐
- 勤奋是一条通往成功的路——阿尔卡特-朗讯测试架构师 郑文强
- velocity基础教程--4通过velocity生成文件--网站静态化实现方案
- SOA架构设计经验分享—架构、职责、数据一致性
- 软件架构师的12项修炼
- 阿里云官方架构图标
- Android系统架构中常用模块清单
- 关于如何隐藏网站项目目录结构的问题
- 第2章 大型网站及其架构演进过程
- 配置高可用的Hadoop平台
- 【读书笔记】大型网站的架构演化,发展历程
- 十年测试经验悟出的测试心得
- 网易视频云分享:1.5亿活跃用户背后的Twitter架构
- 电商网站的初期技术选型【转】
- VS2012发布网站详细步骤
- PHP获取网站中的url
- Android应用内社区SDK技术架构浅析
- 论SOA架构的几种主要开发方式【转】
- 达观数据分析平台架构和Hive实践
- 构建动态网站—javascript的history.go()与history.back()
- Haproxy实现web的高可用及负载均衡群集