文档章节

JVM入门学习之 runtime data area

阳光test
 阳光test
发布于 2015/06/11 21:44
字数 2230
阅读 87
收藏 0
点赞 0
评论 0

        本文主要参考了java6的jvm规范和java8的jvm规范,文档可见:http://docs.oracle.com/javase/specs/jvms/se6/html/VMSpecTOC.doc.htmlhttp://docs.oracle.com/javase/specs/jvms/se8/html/index.html

        这里主要以java6的文档来说,这块的内容详见:http://docs.oracle.com/javase/specs/jvms/se6/html/Overview.doc.html#1732

        runtime data area主要由四部分组成:

        1、pc register : 就是我们常说的pc,指向当前正在执行的代码,由于java是多线程的,而每个核在同一个时刻只有一个线程执行,假设现在我们有线程A和线程B运行在单核的机器上,线程A执行到一半的时候切换到线程B,过了一段时间又切到A,而这个时候系统是需要知道线程A已经执行到什么位置了,所以每一个线程都需要有一个pc register存储当前执行到的代码的地址,如果是native代码,则它是undefined;

        2、method area:存储了类的结构信息等,比如运行时常量池、class属性、方法等,比如常见的字符串String s = "aaaaa";这里的"aaaaa"就是放在运行时常量池中,看着可能比较抽象,等分析字节码的时候就比较清楚了。在Java6时,hotspot vm放在perm gen 去存储,所以很有可能因为工程类过多而导致OOM,可以通过-XX:PermSize和-XX:MaxPermSize来指定大小,不过在Java8中开始采用meta space来存储method area,而它是一个堆外内存,所以不会有这个问题;

        3、stack:这个非常重要,每个线程创建时候都会创建,它存储了stack frame,由于stack只能执行pop、push操作,而每一次方法调用其实就是push一个stack frame,而这个stack frame里面存储了local variable(局部变量表,对应下面代码中的a,b,c变量和this指针)、oprand stack(比如a+b这个操作其实就是入栈a,入栈b然后进行iadd,然后将结果入栈)、    reference to runtime constant pool (这个要分析字节码才比较清楚)、exception table(分析字节码比较清楚)等。


public int test(int a,int b) {
     int c = a + b;
     return c;
}




       4、heap:这个是我们最常用的,存储了绝大部分的对象,比如Test a = new Test();Test对象的实例就存放在heap,为了垃圾回收方便,JVM把这片区域分成了:yong generation(新生代)、tenured generation(老年代)。

          yong generation又被细分为:eden、from survivor(S0)、to survivor(S1),其中S0和S1大小相等,每次系统可用的大小为eden+一个survivor的大小,为什么JVM要把它分成两个survivor,其实是由垃圾收集方法决定的,jvm采用了copying的方式收集yong generation,比如现在eden+S0空间被使用,这个时候yong generation大小不够了,触发了一次YGC,所有存活的对象就会被赶到S1去,然后将S0和eden中的空间全部清理干净,之后系统就直接使用eden+S1来存放对象,S0就变成了备胎。

          在yong generation中存放的的对象,90%以上都是朝生夕死,在下一次YGC的时候就被干掉了,如果这个对象在一次YGC的时候还能够被Gc roots引用到(也就是说这个对象还有用到),那么就将这个对象的年龄+1,当这个对象的年龄达到某一个阀值时,则开始将这个对象转移到tenured。当然,不仅仅是当对象达到年龄的时候才会被转移到tenured generation,当yong generation实在没有空间的情况下也会的,之前我说到过,在YGC时会将还存活的对象从一个survivor复制到另一个survivor,如果存活对象太多,一个survivor放不下的时候,那也会坑。

      在这里,我们用一个简单的Java代码来说明一下,代码如下:

      

public class Test {
	public static void main(String[] args) {
		Test test  = new Test();
		test.test(100L,2);
	}
	public long test(long first,int second) {
		try{
				long third = first + second;
				return third;
		}catch (Exception e){
			e.printStackTrace();
		}
		return 0L;
	}
}

    我们执行javac Test.java编译完之后,执行javap -verbose Test得到 适合人阅读的字节码:

   

Compiled from "Test.java"
public class Test extends java.lang.Object
  SourceFile: "Test.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method	#9.#22;	//  java/lang/Object."<init>":()V
const #2 = class	#23;	//  Test
const #3 = Method	#2.#22;	//  Test."<init>":()V
const #4 = long	100l;
const #6 = Method	#2.#24;	//  Test.test:(JI)J
const #7 = class	#25;	//  java/lang/Exception
const #8 = Method	#7.#26;	//  java/lang/Exception.printStackTrace:()V
const #9 = class	#27;	//  java/lang/Object
const #10 = Asciz	<init>;
const #11 = Asciz	()V;
const #12 = Asciz	Code;
const #13 = Asciz	LineNumberTable;
const #14 = Asciz	main;
const #15 = Asciz	([Ljava/lang/String;)V;
const #16 = Asciz	test;
const #17 = Asciz	(JI)J;
const #18 = Asciz	StackMapTable;
const #19 = class	#25;	//  java/lang/Exception
const #20 = Asciz	SourceFile;
const #21 = Asciz	Test.java;
const #22 = NameAndType	#10:#11;//  "<init>":()V
const #23 = Asciz	Test;
const #24 = NameAndType	#16:#17;//  test:(JI)J
const #25 = Asciz	java/lang/Exception;
const #26 = NameAndType	#28:#11;//  printStackTrace:()V
const #27 = Asciz	java/lang/Object;
const #28 = Asciz	printStackTrace;

{
public Test();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	return
  LineNumberTable: 
   line 1: 0


public static void main(java.lang.String[]);
  Code:
   Stack=4, Locals=2, Args_size=1
   0:	new	#2; //class Test
   3:	dup
   4:	invokespecial	#3; //Method "<init>":()V
   7:	astore_1
   8:	aload_1
   9:	ldc2_w	#4; //long 100l
   12:	iconst_2
   13:	invokevirtual	#6; //Method test:(JI)J
   16:	pop2
   17:	return
  LineNumberTable: 
   line 3: 0
   line 4: 8
   line 5: 17


public long test(long, int);
  Code:
   Stack=4, Locals=6, Args_size=3
   0:	lload_1
   1:	iload_3
   2:	i2l
   3:	ladd
   4:	lstore	4
   6:	lload	4
   8:	lreturn
   9:	astore	4
   11:	aload	4
   13:	invokevirtual	#8; //Method java/lang/Exception.printStackTrace:()V
   16:	lconst_0
   17:	lreturn
  Exception table:
   from   to  target type
     0     8     9   Class java/lang/Exception

  LineNumberTable: 
   line 8: 0
   line 9: 6
   line 10: 9
   line 11: 11
   line 13: 16

  StackMapTable: number_of_entries = 1
   frame_type = 73 /* same_locals_1_stack_item */
     stack = [ class java/lang/Exception ]


}

    

      首先我们看到constant pool中的内容,从const #1到const #28,我们直接看注释,比如const #6,类型是Method,对应着的就是Java代码中的public long test(long first,int second),从这里你就可以理解method area中存放的是啥了把,从method area可以看到类的一个大概,比如类名、方法列表、属性列表等等。

      然后我们看看具体的方法public long test(long first,int second),对应字节码是:

 

public long test(long, int);
  Code:
   Stack=4, Locals=6, Args_size=3
   0:	lload_1
   1:	iload_3
   2:	i2l
   3:	ladd
   4:	lstore	4
   6:	lload	4
   8:	lreturn
   9:	astore	4
   11:	aload	4
   13:	invokevirtual	#8; //Method java/lang/Exception.printStackTrace:()V
   16:	lconst_0
   17:	lreturn
  Exception table:
   from   to  target type
     0     8     9   Class java/lang/Exception

  LineNumberTable: 
   line 8: 0
   line 9: 6
   line 10: 9
   line 11: 11
   line 13: 16

  StackMapTable: number_of_entries = 1
   frame_type = 73 /* same_locals_1_stack_item */
     stack = [ class java/lang/Exception ]


}




    我说过stack frame中存储了local variable,oprand stack等,我们来看看local variable,这里的变量有那些呢,首先参数中有long first,int second这两个,然后还有方法体中的long third和Exception e,那还有吗,其实还有一个,就是this,它是隐含在内的,我们看到字节码中有一个Locals=6,那这个代表什么呢,其实是代表局部变量有6个slot,JVM定义了在32位系统中32bit及小于32位的变量占用一个slot,而64位的占用两个slot,比如int,char,Object的引用,都是1个word,而long,double占用两个slot,多说一点,long,double JVM并不保证它是原子的。那么我们看看现在的局部变量:long first(2 slot) + int second (1 slot) + this (1 slot) + long third (2 slot) + Exception e (1 slot) = 7 slot,什么情况,为什么比字节码中多了一个slot?

     原来JVM会使用slot来复用local variable,怎么理解,很简单,在局部变量表中,如果一个这个变量已经过了作用域,那么后续的局部变量可以复用它的位置,比如这个例子里面的long third = first + second;third这个变量在Exception e这个变量使用时其实已经过了作用域,而它本来占用的local variable的位置为4和5,所以Exception e这个变量占用的是位置4,所以加起来总共locals=6,大家可以将long third = first + second;提到try上面去试试,locals会变成7。

      我们看到上面字节码中有exception table,这个是干啥的,其实很简单,就是说只要字节码0-8,如果出现了异常类型为java/lang/Exception的异常,则跳转到字节码9,这里首先astore 4然后aload 4,都是对局部变量表的下标为4的元素操作,也可以间接证明之前说的slot复用的问题。

      我们现在看看字节码:


13:	invokevirtual	#8; //Method java/lang/Exception.printStackTrace:()V



      这里对应的Java代码就是



e.printStackTrace();



     我们知道#8在const pool中,而方法调用其实是在stack中,我们也就能够理解stack中的referene to runtime constant pool了。


    我是初学者,有写错的地方欢迎大家拍砖!

 



  








© 著作权归作者所有

共有 人打赏支持
阳光test

阳光test

粉丝 543
博文 71
码字总数 91741
作品 1
杭州
程序员
JVM Specification 第一天(JVM数据类型&运行时数据区)

1.The Structure of the Java Virtual Machine 1.1 Data Type 1 The booleanType Although the Java virtual machine defines a boolean type, it only provides very limited support for i......

Kerry_Han
2014/03/18
0
0
ByteBuffer.allocate() vs. allocateDirect

Operating systems perform I/O operations on memory areas. These memory areas, as far as the operating system is concerned, are contiguous sequences of bytes. It's no surprise th......

pczhangtl
2014/03/30
0
0
JVM系列-Java内存区域(一)

运行时数据区域 Java虚拟机在Java程序执行过程中会把它所管理的内存划分为若干个数据区域,有的区域随着虚拟机进程的启动而存在,有的区域依赖用户线程的启动和结束而建立和销毁。 RUNTIME ...

stefanzhlg
2014/08/29
0
0
Android 之 内存管理

概述 在android的开发中,要时刻主要内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的,在google的G1中,分配的最大堆大小只有16M,后来的机器一般都为24M,实在是少的...

等待流星
2014/03/08
0
0
java 虚拟机--新生代与老年代GC

1. Java堆中各代分布: 图1:Java堆中各代分布 Young:主要是用来存放新生的对象。 Old:主要存放应用程序中生命周期长的内存对象。 Permanent:是指内存的永久保存区域,主要存放Class和M...

SunnyWu
2014/10/16
0
4
bytebuffer vs c++ malloc

Java equivalents of malloc(), new, free() and delete (ctd) Continued from our introduction to memory management operators in C/C++ and Java. A Java equivalent of the malloc() fu......

不道归来
2017/10/23
0
0
深入了解java运行时的内存区域

对于java程序员来说,并不必显示地对内存进行管理,一切都交给java虚拟机去做吧,而且,你也不一定做得比java虚拟机来得专业。好像所有内存管理都交给虚拟机去做就万事大吉了,但是,事实有时...

依然范特西
2012/09/26
0
0
Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区
05/09
0
0
怎样学习java,漫谈java学习之路

Java - 近10年来计算机软件发展过程中的传奇,其在众多开发者心中的地位就如“屠龙刀”、“倚天剑”。 Java是个平台,我只想说说我对学好Java的一点看法,希望对初学者有所帮助。 1. 思考一下...

jccur
2011/08/26
0
9
慕课网Spark SQL日志分析 - 5.DateFrame&Dataset

5.DateFrame&Dataset 1.DateFrame产生背景 DataFrame 不是Spark Sql提出的。而是在早起的Python、R、Pandas语言中就早就有了的。 Spark诞生之初一个目标就是给大数据生态圈提供一个基于通用语...

Meet相识_bfa5
07/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

python以太坊类库web3.py概览

python通过web3.py库与以太坊交互共同入口是web3对象。web3对象提供API接口,python开发应用与以太坊进行交互如钱包创建、支付、转账等连接JSON-RPC服务器进行。 Providers提供者 Providers使...

智能合约
2分钟前
0
0
【Android学习笔记】设置App启动页

先将启动页放到项目资源中,图片一般是1080*1920的jpg。 新建一个activity,如图: 创建成功之后,打开刚刚创建的activity,来进行代码的编写: >>>阅读全文

全部原谅
3分钟前
0
0
什么是React-redux、为什么使用React-redux、怎么使用React-redux

1、什么是React-redux React-redux是用于连接React和Redux的 2、为什么使用React-redux 使用React-redux可以使redux部分代码更简洁更明了,比如组建中需要使用到的数据都在mapStateToProps方...

kimyeongnam
6分钟前
0
0
Spring核心——Stereotype组件与Bean扫描

在注解自动装载中介绍了通过注解(Annotation)自动向Bean中注入其他Bean的方法,本篇将介绍通过注解(Annotation)向容器添加Bean的方法。 Spring的核心容器提供了@Component和@Bean注解来标...

随风溜达的向日葵
6分钟前
0
0
利用世界杯,读懂 Python 装饰器

Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性, 熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。 今天就结合最近的世界杯带大家理解下装饰器。...

猫咪编程
9分钟前
0
0
flink fold example

flink fold例子 import org.apache.flink.api.common.functions.*;import org.apache.flink.streaming.api.datastream.DataStream;import org.apache.flink.streaming.api.environment.S......

coord
11分钟前
0
0
c++ qt 组播总结

每个人都有不同的认知规律和习惯, 有的人喜欢搞一套严密的大理论, 论述起来滔滔不绝, 不管自己懂不懂, 反正读者/听者是没搞懂。 有的人喜欢从实践出发, 没看到代码, 不运行一下, 不看...

backtrackx
15分钟前
0
0
Sublime text2安装json格式化插件SublimePrettyJson[Windows]

一、下载SublimePrettyJson插件包 https://github.com/dzhibas/SublimePrettyJson 二、将下载的文件解压放到在package目录下面 C:\Users\lucky\AppData\Roaming\Sublime Text 3\Packages 每个......

lazy~
15分钟前
0
0
安装vue-cli 报4058错误

1. 4058是网络代理错误。 安装淘宝源修改一下就可以了: npm --registry https://registry.npm.taobao.org info underscore 改为cnpm执行: cnpm install --global vue-cli 安装成功: 试试版...

MrBoyce
16分钟前
0
0
CPU飙升分析

1、top -----看具体的进程 2、top -H -p pid ------该进程的线程 3、printf 0x%x 15248 ------将线程改为16进制 4、jstack 进程...

北极之北
19分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部