首页 > 代码库 > Java虚拟机内存模型
Java虚拟机内存模型
1)程序计数器:线程私有
当线程数量超过CPU数量时,线程之间根据时间片轮询抢夺CPU资源,对于单核CPU来说,每一个时刻,只能有一个线程在运行,而其他线程必须被切换出去。因此,每个线程都有一个独立的程序计数器,用于记录下一条要运行的指令,各个线程之间的计数器互不影响,是一块线程的私有内存空间。当一个线程正在执行一个Java方法时,程序计数器记录正在执行的Java字节码地址,如果执行的是native方法,则计数器为空
2)Java虚拟机栈:线程私有
同Java线程同时间创建,用于保存方法的局部变量,部分结果,并参与方法的调用和返回。Java虚拟机定义了两种与栈空间有关的异常:StackOverflowError,OutOfMemoryError。当请求的栈深度大于最大可用的栈深度,抛出StackOverflowError。如果栈可以动态扩展,扩展栈的过程中,没有足够的内存空间来支持栈的扩展(系统内存不足?),则抛出OutOfMemoryError。以下为一个抛出java.lang.StackOverflowError的实例
public class TestStack{ private static int count=0; public static void recursion(long a,long b,long c){ long d=0; long e=0; long f=0; count++; recursion(a,b,c); } public static void main(String []args){ try{ recursion(1L,2L,3L); }catch(Throwable e){ System.out.println("count:"+count); System.out.print(e); } } public void recursion1(long a,long b,long c){ long d=0; long e=0; long f=0; count++; recursion1(a,b,c); } } /* 直接运行 输出: count:4927 使用-Xss来扩大栈空间,运行 java -Xss1m TestStack 输出: count:27590 */
虚拟机栈在运行时使用了一种叫做栈帧的数据结构保存上下文数据,在栈帧中,存放了方法的局部变量表、操作数栈、动态链接方法和返回地址等信息。每个方法的调用都伴随着栈帧的入栈操作,方法的返回则表示栈帧的出栈操作。方法调用时,方法的参数和局部变量越多,局部变量表就越大,栈帧相应变大以满足方法调用所需传递的信息。因此,单个方法调用所需的栈空间大小也会比较多
结论:使用-Xss来扩大栈空间,增加栈空间大小后,函数调用深度上升。函数的局部变量越多,栈帧就越大,单次函数调用对栈空间的需求也会增加,函数嵌套调用次数减少
局部变量表的空间是可重用的,对于一个方法所分配的最大局部变量表的容量,可以使用jclasslib工具来查看
public class Test1{ public static void main(String []args){ test3(); } public static void test1(){ { byte[] b=new byte[6*1024*1024]; } //此时b仍然在该栈帧的变量表中, //GC根可以引用到该内存块,又未能有足够多的局部变量来服用该内存块, //因此不会被回收[1] System.gc(); System.out.println("first explict gc over"); } public static void test2(){ { byte[] b=new byte[6*1024*1024]; b=null;//帮助系统GC } //被回收[2] System.gc(); System.out.println("first explict gc over"); } public static void test3(){ { byte[] b=new byte[6*1024*1024]; } int a=0;//服用b的内存块,GC根无法找到b,因此被回收[3] System.gc(); System.out.println("first explict gc over"); } } /* java -verbose:gc Test1 --[1] [GC 224K->134K(5056K), 0.0027563 secs] [Full GC 134K->134K(5056K), 0.0177525 secs] [Full GC 6278K->6278K(11204K), 0.0151799 secs] first explict gc over --[2] [GC 224K->134K(5056K), 0.0027134 secs] [Full GC 134K->134K(5056K), 0.0178466 secs] [Full GC 6278K->134K(11204K), 0.0152811 secs] first explict gc over --[3] [GC 224K->134K(5056K), 0.0027211 secs] [Full GC 134K->134K(5056K), 0.0174787 secs] [Full GC 6278K->134K(11204K), 0.0147169 secs] first explict gc over */
3)本地方法栈:
与Java虚拟机栈功能相似,Java虚拟机栈用于管理Java函数调用,而本地方法栈用于管理本地方法的调用,有C实现。在Hot Spot虚拟机中不区分本地方法栈和Java虚拟机栈,因此,同样也会抛出StackOverflowError,OutOfMemoryError异常
4)Java堆:Java运行时内存中最重要的部分
分配运行时所有的对象和数组。分为新生代(eden,survivor space0[s0,from space],survivor space1[s1,to space])和老年代。
5)方法区:线程共享
方法区存放的是类的类型信息,常量池,域信息,方法信息等大部分来自class文件的信息。在Hot Spot虚拟机中,方法区也叫做永久区,是一块独立于Java堆的内存空间,同样也可以被GC回收,只是表现和Java堆不同
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space public class PermGenGC{ public static void main(String []args){ List<String> list=new ArrayList<String>(); int i; for(i=0;i<Integer.MAX_VALUE;i++){ //如果常量池中存在当前String,则返回池中对象,如果不存在,则先将String加入到常量池,再返回池中对象引用 list.add(String.valueOf(i).intern());//加入到常量池 } } } /* java -XX:PermSize=2m -XX:MaxPermSize=4m PermGenGC Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at PermGenGC.main(PermGenGC.java:7) java -XX:PermSize=2m -XX:MaxPermSize=4m -XX:+PrintGCDetails PermGenGC [GC [DefNew: 896K->64K(960K), 0.0043064 secs] 896K->180K(5056K), 0.0050202 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC [DefNew: 919K->0K(960K), 0.0031302 secs] 1035K->236K(5056K), 0.0039759 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] [GC [DefNew: 778K->0K(960K), 0.0029668 secs] 1015K->390K(5056K), 0.0036493 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [DefNew: 896K->0K(960K), 0.0030697 secs] 1286K->619K(5056K), 0.0037499 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [DefNew: 896K->0K(960K), 0.0033067 secs] 1515K->964K(5056K), 0.0040117 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC [Tenured: 964K->581K(4096K), 0.0595687 secs] 1665K->581K(5056K), [Perm: 4095K->4095K(4096K)], 0.0610942 secs] [Times: user=0.06 sys=0.00, real=0.06 secs] [Full GC [Tenured: 581K->581K(4096K), 0.0591349 secs] 581K->581K(5056K), [Perm : 4095K->4095K(4096K)], 0.0636965 secs] [Times: user=0.06 sys=0.01, real=0.06 secs] Exception in thread "main" [Full GC [Tenured: 581K->236K(4096K), 0.0289138 secs] 596K->236K(5056K), [Perm : 4095K->360K(4096K)], 0.0332241 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at PermGenGC.main(PermGenGC.java:8) */
Java虚拟机内存模型