深入理解Java内存模型
Java虚拟机的运行时内存按照类型可分为5部分:Java方法区、Java栈、Native方法区、Java堆和程序计数器。 其中栈和程序计数器不能跨线程访问。程序计数器:是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,而且永远不会发生Out of Memory问题,其它四种内存区域都可能出现OOM现
Java虚拟机的运行时内存按照类型可分为5部分:Java方法区、Java栈、Native方法区、Java堆和程序计数器。 其中栈和程序计数器不能跨线程访问。
程序计数器:是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,而且永远不会发生Out of Memory问题,其它四种内存区域都可能出现OOM现象。
Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同, 跟C语言类似不使用new关键字的变量存放在栈里。在栈里可能出现StackOverflowException或者OOM异常。
本地方法栈: 就是Native层用C语言编写的栈(没调用new),其它跟Java栈类似。
Java堆:所有的java队形实例、数组都在堆上new出来,是垃圾收集器(Garbage Collection)管理的主要区域。Java堆可以处在物理不连续的内存空间中, 但逻辑上要是连续的。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。在日常编程中遇到的OOM异常大都发生在Java堆。
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
上面是JVM的内存模型, 那么一个Java对象在内存里分为几部分呢?
答案:对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
那么JVM是如何回收内存的呢?
一、引用计数法: 当指向的Java对象的引用计数为0时, 在下次GC回收时会被释放。
考虑问题: 如果出现相互引用, 即A指向B,B指向A, 那么GC能回收吗?
public static class ReferenceCountingGC{
public Object instance=null;
private final int ONE_MB=1024*1024;
private byte[]bigSize=new byte[1*ONE_MB]; //只为了占空间
public static void testGC(){
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
//假设在这行发生GC,objA和objB是否能被回收?
long begin = Runtime.getRuntime().totalMemory();
System.out.println("befor:" + begin);
System.gc();
long after = Runtime.getRuntime().totalMemory();
System.out.println("after:" + after);
System.out.println("diff:" + (after-begin)); //判断到底释放了多少
}
}
执行结果:
befor:16252928
after:16318464
diff:65536
GC前后差了64K字节, 而objA和objB都大于1M字节, 现实没有被释放! 所以编程中切忌不要相互强引用!!!
二、可达性分析算法
基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。
在Java语言中,可作为GC Roots的对象包括下面几种:虚拟机栈(栈帧中的本地变量表)中引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI(即一般说的Native方法)引用的对象。
关于内存基础知识就说这么多, 下一篇准备写如何计算Java对象的大小。
更多推荐
所有评论(0)