1.概述
Java虚拟机运行的数据区域分为:线程隔离的数据区和非线程隔离的数据区。线程隔离的数据区包括:程序计数器、虚拟机栈、本地方法栈。所有线程共享的数据区包括:堆和方法区。
2.各个区域介绍
2.1.程序计数器
程序计数器是当前线程执行的字节码的行号指示器。执行Java方法时,它记录的是正在执行的虚拟机字节码指令的地址。执行native方法时,计数器的值为null。
异常:唯一一个不会抛出OutofMemoryError的内存数据区域。
注:”native method”:Java调用非Java代码的接口。
2.2.虚拟机栈
描述Java方法执行的内存模型,Java方法执行时会创建栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等。其中局部变量表存放编译期的基本数据类型和对象引用类型。
异常:StackOverFlowError和OutOfMemoryError。StackOverFlowError:线程请求的栈的深度大于虚拟机所允许的深度时出现。OutOfMemoryError:虚拟机动态扩展时无法申请到足够的内存时出现。
2.3.本地方法栈
作用和虚拟机栈类似,区别是:虚拟机栈执行的是Java方法,本地方法栈执行的是native method。
2.4.堆
在虚拟机启动的时候就会创建堆,它主要存放对象实例,是垃圾收集的主要区域。它分为新生代和老年代。其中新生代又具体划分为Eden空间、From Survior空间、To Survior空间。 异常:OutOfMemoryError:堆中没有完成实例分配,又无法再扩展。
2.5.方法区
存放已经被虚拟机加载的类信息、常量、静态变量(编译后的代码)。运行时常量池:也是方法区的一部分,用于存放编译器生成的各种字面变量和符号引用。
异常:OutOfMemoryError:当方法区无法满足内存分配的需求时。
3.虚拟机参数
-Xms 设置堆的最小值,比如-Xms5m
-Xmx 设置堆的最大值,比如-Xmx20m
注意:在实际开发中一般将-Xms和-Xmx的大小设置为相同,这样的好处是减少程序运行时的垃圾回收次数,从而提高性能。
-XX:+HeapDownOutOfMemoryError 内存溢出时导出整个堆得信息。
-XX:+HeapDumpPath 设置导出堆的存放路径。
-Xss 设置栈的内存容量大小,比如-Xss128k,它直接决定函数可调用的深度。
-XX:PermSize和-XX:MaxPermSize 限制方法区的大小(也可间接限制常量池的容量)。
-XX:MaxDirectoryMemoryError 直接内存大小(如果不设定默认等于-Xmx)。
-Xmn 设置新生代的大小,比如-Xmn10M
-XX:+UseSerialGC 配置的垃圾收集器为serial垃圾收集器。
-XX:SurviorRatio 设置新生代中Eden空间和from survior/to survior 之间的比例。即-XX:SurviorRatio=eden/from=eden/to,如果-XX:SurviorRatio=8,说明三者比例为8:1:1.
-XX:NewRatio 设置新生代和老年代的比例,比如-XX:NewRatio=3,一般新生代和老年代的比例为1:3或1:4。
-XX:MaxTenuringThreshold 设置晋升老年代的阈值。
-XX:+PrintGC 虚拟机启动后只要遇到GC就会打印日志。
-XX:+PrintGCDetails 查看各个区的详细信息。
4.hotSpot虚拟机对象探秘
4.1.对象的创建
即”A a = new A();”的过程。
(1)虚拟机遇到一条new指令时,首先首先执行类加载检查:检查指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过。
(2)为新生对象分配内存(在堆中进行)。分配策略:java堆是否规整,若规整则采用指针碰撞的方式,否则采用空闲列表的方式。java堆是否规整由采用的垃圾收集器是否带有压缩整理功能决定。
Serial、ParNew等带compact过程的采用指针碰撞,CMS采用空闲列表。同时分配内存的过程会产生线程安全的问题,可通过同步解决或者把内存分配的动作按照线程动态的划分到不同的空间中进行。
(3)将分配到的内存空间初始化为零值,这样保证了对象的实例字段在java代码中可以不赋初始值就直接使用。
(4)对对象进行必要的设置,例如这个对象是哪个类的实例、怎样才能找到类的元数据信息、对象的哈希码等。
(5)执行init方法,所有的字段都还为零,并按照程序员的意愿进行初始化。
4.2.对象的内存布局
对象在内存中存储的布局可以分为3个区域:对象头、示例数据和对齐填充。
对象头:包括两部分,第一部分用于存储对象自身的运行时数据,如哈希吗、线程持有的锁、GC分代年龄等;另一部分是类型指针(对象指向它的类元数据的指针),通过这个指针可以确定这个对象时哪个类的实例。
实例数据:程序代码中所定义的各种类型的字段内容,包括从父类继承的和子类定义的。
对齐填充:这部分可有可无,起到占位符的作用。
4.3.对象的访问定位
对象创建后,需要通过栈上的reference数据来操作堆上的实例数据,堆又操作方法区的对象类型信息。访问定位方式有两种:句柄和直接指针。