java虚拟机内存模型的组成部分以及各个部分的作用:
程序计数器,虚拟机栈,本地方法栈,java堆,方法区。
程序计数器:记录下一条要运行的命令,因为java是支持多线程的,当线程数量超过CPU数量的时候,各个线程通过时间片轮询的方式获取CPU资源,每一个时刻,只有一个线程在运行,为此,每一个线程必须有一个独立的pc,用于记录下一条要运行的命令。各个线程的pc相互独立,互不干扰,是线程私有的内存空间。当程序正在运行的是一个java方法,pc记录的是正在运行的java字节码地址,如果正在运行的是Native方法的时候,pc为空。
虚拟机栈:也是线程的私有内存空间,与线程一同创建,保存方法的局部变量和部分结果,参与方法的调用和返回。java虚拟机允许虚拟机栈大小是动态的活着是固定的,与栈有关的异常有两种,一种是StackOverFlowError和OutOfMemoryError,线程在计算中,如果请求的栈深度大于最大深度的话,就会报StackOverFlowError,也可以动态扩展栈大小,如果没有足够的内存空间支持扩展栈的话,就会报OutOfMemoryError.
此外要说明的是,虚拟机栈在运行的时候使用了一种叫做栈帧的数据结构来保存上下文数据,栈帧里面保存有局部变量表,操作数,动态连接方法,方法返回地址等信心。调用方法对应的就是栈帧的入栈和出栈,如果方法的参数和局部变量比较多的话,栈帧就会比较大,这样所需要提供的栈空间也比较多。
package com.wangbiao.performance.virtualStack;
/**
*
* @Title: TestVirtualStack.java
* @Package com.wangbiao.performance.virtualStack
* @Description: 通过jclasslib工具分析java虚拟机栈
* @author wangbiao
* @date 2014-10-13 下午4:53:26
* @version V1.0
*/
public class TestVirtualStack {
private static int count =0;
public static void recursion(long a ,long b,long c){
long d,e,f =0;
count++;
recursion(a,b,c);
}
public static void testMethodDeep(){
try {
recursion(1L,2L,3L);
} catch (Throwable e) {
System.out.println("Deep of the virtual stack is : "+count);
e.printStackTrace();
}
}
public static void main(String[] args) {
testMethodDeep();
/**
java.lang.StackOverflowError
Deep of the virtual stack is : 10505
*/
/**
* 增大Xss后的结果
java.lang.StackOverflowError
Deep of the virtual stack is : 24052
*/
/**
* 增大局部变量表的结果
* Deep of the virtual stack is : 8876
java.lang.StackOverflowError
*
*/
}
}
局部变量表就是存放方法的参数和方法内部所用到的局部变量,操作数栈就是存放中间结果,返回地址就是方法被调用的地址;
动态链接方法就是说如果栈帧A的方法在调用栈帧B的方法,则虚拟机会将栈帧B的符号应用作为参数,但是符号应用并不是内存地址,所以需要将符号引用转化为直接引用,如果符号引用是在类加载阶段或者第一次使用的时候转化为直接应用,那么这种转换成为静态解析,如果是在运行期间转换为直接引用,那么这种转换就成为动态连接。
本地方法栈:本地方法栈跟Java虚拟机栈的功能很相似,不同的是本地方法栈调用的是本地方法,是C实现的,同样会有tackOverFlowError和OutOfMemoryError。
Java堆:java堆是java运行时最重要的内存空间,几乎所有的对象和数组都是在堆中分配空间的,java堆分为新生代和老年代。新生代又细分为eden,survivor space0(s0或者from space)和survivor space1(s1或者to space).在s0和s1中的对象至少经过一次垃圾回收,并且得以幸存,幸存区的对象在一定时间仍未被回收,则有机会进入老年代tenured.
方法区:方法区也是JVM内存中比较重要的一块内存区域,与java堆一样,也是所有线程共享的,方法区主要保存的是类的元数据。
方法区中最为重要的是类的类型信息,常量池,域信息,方法信息.类型信息包括类的完整名称,父类的完整名称,类型修饰符和类型的直接接口类表;常量池包括这个类方法,域等信息所有引用的常量信息;域信息包括域名称,域类型和域修饰符;方法信息包括方法名称,方法参数,方法修饰符,方法返回类型,方法字节码,操作数栈和方法栈帧的局部变量区大小和异常表。
package com.wangbiao.performance.permanent;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
/**
*
* @Title: TestPermanentZone.java
* @Package com.wangbiao.performance.permanent
* @Description: TODO
* @author wangbiao
* @date 2014-11-30 下午3:39:11
* @version V1.0
*/
public class TestPermanentZone {
private static MyClassLoader myClassLoader = new MyClassLoader();
/**
* 对常量池的回收
*/
public static void testConstantPoolGc(){
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String str = String.valueOf(i).intern();
}
// [Full GC [Tenured: 0K->150K(10944K), 0.0095386 secs] 4022K->150K(15872K), [Perm : 4095K->373K(4096K)], 0.0097295 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
// [Full GC [Tenured: 150K->150K(10944K), 0.0086481 secs] 3980K->150K(15936K), [Perm : 4095K->373K(4096K)], 0.0088300 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
// [Full GC [Tenured: 150K->150K(10944K), 0.0088304 secs] 3956K->150K(15936K), [Perm : 4095K->373K(4096K)], 0.0090316 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
// [Full GC [Tenured: 150K->150K(10944K), 0.0088140 secs] 3884K->150K(15936K), [Perm : 4095K->373K(4096K)], 0.0089971 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// [Full GC [Tenured: 150K->150K(10944K), 0.0111478 secs] 3894K->150K(15936K), [Perm : 4096K->373K(4096K)], 0.0113498 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
// [Full GC [Tenured: 150K->150K(10944K), 0.0090061 secs] 3901K->150K(15936K), [Perm : 4096K->373K(4096K)], 0.0091802 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
}
/**
* 对类的元数据回收
* @throws Exception
*/
public static void testClassDefinitionInfoGc() throws Exception{
for (int i = 0; i < Integer.MAX_VALUE; i++) {
CtClass c = ClassPool.getDefault().makeClass("DynamicClass"+i);
c.setSuperclass(ClassPool.getDefault().get("com.wangbiao.performance.permanent.TestPermanentZone"));
Class clz = c.toClass();
TestPermanentZone temp = (TestPermanentZone) clz.newInstance();
}
// [GC [DefNew: 4416K->512K(4928K), 0.0063036 secs] 4416K->1651K(15872K), 0.0063340 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
// [GC [DefNew: 4928K->512K(4928K), 0.0104619 secs] 6067K->4244K(15872K), 0.0104881 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
// [GC [DefNew: 4928K->511K(4928K), 0.0118613 secs] 8660K->6890K(15872K), 0.0118839 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
// [GC [DefNew: 4927K->512K(4928K), 0.0111606 secs] 11306K->9539K(15872K), 0.0111790 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
// [Full GC [Tenured: 9027K->10943K(10944K), 0.0345790 secs] 11993K->10970K(15872K), [Perm : 4095K->4095K(4096K)], 0.0346480 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
// [Full GC [Tenured: 10943K->10970K(18240K), 0.0313195 secs] 10970K->10970K(26496K), [Perm : 4095K->4095K(4096K)], 0.0313523 secs] [Times: user=0.03 sys=0.00, real=0.04 secs]
// [Full GC [Tenured: 10970K->10971K(18240K), 0.0321393 secs] 11120K->10971K(26496K), [Perm : 4095K->4095K(4096K)], 0.0321721 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
// [Full GC [Tenured: 10971K->10871K(18240K), 0.0372331 secs] 10971K->10871K(26496K), [Perm : 4095K->4089K(4096K)], 0.0372647 secs] [Times: user=0.05 sys=0.00, real=0.04 secs]
// Exception in thread "main" javassist.CannotCompileException: by java.lang.OutOfMemoryError: PermGen space
// at javassist.ClassPool.toClass(ClassPool.java:1051)
// at javassist.ClassPool.toClass(ClassPool.java:994)
// at javassist.ClassPool.toClass(ClassPool.java:952)
// at javassist.CtClass.toClass(CtClass.java:1079)
// at com.wangbiao.performance.permanent.TestPermanentZone.testClassDefinitionInfoGc(TestPermanentZone.java:42)
// at com.wangbiao.performance.permanent.TestPermanentZone.main(TestPermanentZone.java:52)
}
/**
* 对类的元数据回收
* @throws Exception
*/
public static void testClassDefinitionInfoGc2() throws Exception{
for (int i = 0; i < Integer.MAX_VALUE; i++) {
CtClass c = ClassPool.getDefault().makeClass("DynamicClass"+i);
c.setSuperclass(ClassPool.getDefault().get("com.wangbiao.performance.permanent.TestPermanentZone"));
Class clz = c.toClass(myClassLoader,null);
TestPermanentZone temp = (TestPermanentZone) clz.newInstance();
if(i%10 == 0){
myClassLoader = new MyClassLoader();
}
}
// [GC [DefNew: 4416K->512K(4928K), 0.0078837 secs] 4416K->1624K(15872K), 0.0079117 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
// [GC [DefNew: 4928K->511K(4928K), 0.0104212 secs] 6040K->4198K(15872K), 0.0104450 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
// [GC [DefNew: 4927K->512K(4928K), 0.0114738 secs] 8614K->6826K(15872K), 0.0114935 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
// [GC [DefNew: 4928K->512K(4928K), 0.0125683 secs] 11242K->9447K(15872K), 0.0125921 secs] [Times: user=0.00 sys=0.02, real=0.02 secs]
// [Full GC [Tenured: 8935K->10452K(10944K), 0.0326500 secs] 12444K->10452K(15872K), [Perm : 4095K->1497K(4096K)], 0.0328569 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
// [GC [DefNew: 7104K->832K(7936K), 0.0157929 secs] 17556K->14728K(25360K), 0.0158118 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
// [GC [DefNew: 7936K->832K(7936K), 0.0185049 secs][Tenured: 18112K->18192K(18192K), 0.0393760 secs] 21832K->18395K(26128K), [Perm : 3979K->1493K(4096K)], 0.0581272 secs] [Times: user=0.05 sys=0.00, real=0.06 secs]
// [GC [DefNew: 12224K->1472K(13696K), 0.0314352 secs] 30416K->25647K(44016K), 0.0314562 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
// [Full GC [Tenured: 24175K->26458K(30320K), 0.0511984 secs] 28676K->26458K(44016K), [Perm : 4095K->1660K(4096K)], 0.0514377 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]
// [Full GC [Tenured: 26458K->32773K(44100K), 0.0849732 secs] 40370K->32773K(64004K), [Perm : 4095K->857K(4096K)], 0.0851986 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
// [Full GC [Tenured: 32773K->42551K(54624K), 0.0906664 secs] 51603K->42551K(79264K), [Perm : 4095K->862K(4096K)], 0.0909213 secs] [Times: user=0.09 sys=0.00, real=0.09 secs]
// [Full GC [Tenured: 42551K->52512K(70920K), 0.1059572 secs] 61607K->52512K(102920K), [Perm : 4095K->862K(4096K)], 0.1062167 secs] [Times: user=0.09 sys=0.02, real=0.10 secs]
}
public static void main(String[] args) throws Exception {
//方法区也可称为永久区,主要存放常量和类的定义信息。
//在HotPot虚拟机确认某一个类信息不会被使用,也会将其回收,
//回收的基本条件至少有:所有该类的实例被回收,且装载该类的ClassLoader被回收。
//testConstantPoolGc();
//Full GC在这种情况下无法回收类的元数据
//testClassDefinitionInfoGc();
//
testClassDefinitionInfoGc2();
}
}
class MyClassLoader extends ClassLoader{
}
附:GC日志各个值
R(n) = T(n): [ <GC> HB->HE(HC), D]
n 清单中记录的索引,1 是第一个,m 是最后一个
R(n) GC 记录
T(n) 第 n 个 GC 发生的时间
HB GC 之前堆的数量
HE GC 之后使用的堆数量
HC 堆空间的总量
D GC 周期的持续时间