博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解Java内存模型
阅读量:2350 次
发布时间:2019-05-10

本文共 2062 字,大约阅读时间需要 6 分钟。

            

      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对象的大小。

你可能感兴趣的文章
Python3---获取延迟、提前的时间、日期---datetime、time
查看>>
Python3+selenium+Chrome---获取表格(tbody)中数据(tr)的详细内容----a & td内容的获取
查看>>
Docker/Podman使用提高----Dockerfile的制作基础及常见的问题
查看>>
Jenkins持续部署---centos7+Docker+Github+Flask项目-------补丁篇
查看>>
C语言基础---指针数组----初始化方式&常量指针数组、指针常量数组
查看>>
C语言基础---数组、指针之间的相同与不同
查看>>
类的继承的应用场景
查看>>
python3 + selenium------Chrome和Firefox 驱动的使用和版本对应
查看>>
pycharm不同测试框架的设置、unittest测试案例
查看>>
python unittest TestCase间共享数据(全局变量的使用)
查看>>
Python中普通字符串 & json字符串&json对象的区别
查看>>
python中json.dumps()和json.dump() 以及 json.loads()和json.load()的区分
查看>>
Python3中打开文件的方式(With open)
查看>>
python中unittest加载测试用例的4种方法
查看>>
iOS中使用RNCryptor对资源文件加密
查看>>
Device Tree编译工具dtc
查看>>
softlockup/hardlockup原理详细介绍
查看>>
hash表的C实现和hash桶的示例
查看>>
系统登录探究——(三)自动登录
查看>>
系统登录探究——(四)找回密码
查看>>