盒子
盒子

JAVA进阶之JVM浅析内存模型

JAVA与C++之间有一堵由内存动态分配和垃圾收集技术所围城的高墙,墙外的人想进去,墙里的人却想出来。
这是出自《深入理解JAVA虚拟机》中的一句话,实际上说的确实没错。对于做C++开发来说,内存何时分配、何时收回这些都是开发人员需要自己关心的,而这些确实是非常头痛的事情,开发人员多么希望摆脱这些。对于JAVA开发来说,GC承担了内存的分配和回收,开发人员不在需要去关心。Google一下JVM会发现相关的信息大约又4,430,000条, 市面上相关的书也非常的多,这说明开发人员想了解JAVA的内存分配和回收机制

事实上开发人员也应该去了解JAVA的内存回收机制,因为只有更好的了解JVM的机制,这样才能让它发挥出更大优势。这就好像把一件很重的事儿交给一个人处理,虽然知道这个人很可靠。但不知道它的脾气、做事儿方式,有可能在交代事儿的时候触犯某些地方,可能最终导致不能很流畅的处理。

JVM是Java virtual machine的简称,需要了解的主要是内存模型和GC这量大块儿。

JVM的内存模型分为:

  • 程序计数器
    非常小的一块儿内存空间,主要作用认为是记录当前线程执行的字节码的行号。并且每一个线程都有一个独立的程序计数器。线程执行一个方法的时候,计数器记录正在执行字节码指令的地址;执行Native方法时则为空。这块儿空间也是JVM规范中唯一块儿没有规定OOM的空间。

  • 虚拟机栈和本地方法栈
    虚拟机栈是线程私有的内存空间,因此生命周期与线程相同。每个方法被执行的时候都会同时创建一个栈帧,主要是存放局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用到执行完成,这个过程对应一个栈帧在虚拟机栈中从入栈到出栈的过程。这个局部变量表存储了编译期可知的基本数据类型以及对象引用和returnAddress类型。这个内存空间会抛出StackOverflowError异常和OOM异常,前者在线程请求栈的深度超过最大深度时抛出,后者在虚拟机无法申请到内存时抛出。

本地方法栈跟虚拟机栈类似,只是虚拟机栈服务JAVA方法,本地方法栈服务Native方法。抛出的异常也跟虚拟机栈一样。Sun的JVM中这两个空间没有区分。

  • 堆内存
    很重要的一块儿内存空间,在上学的时候老师讲内存空间的时候就把JVM内存模型简单分为堆内存和栈,主要是这两块儿很最重要也是最常接触的。堆内存是JVM内存模型中最大的一块儿,并且是被所有线程共享的。主要存放的是对象的实例,JVM规范中说所有的对象实例和数组都要在堆上分配,但这个现在也不是绝对的。这个内存空间也是GC主要的工作区域。因此在JVM调优的过程中,这个内存空间也是要重点照顾的区域。

jvm-heap

如上图所示,堆内存又细分为年轻代(Young)和老年代(Old)两块儿,而年轻代又分为一个Eden区、一个From Survivor和一个To Survivor区域,新创建的对象主要是在Eden区域。

  • 方法区
    这个内存块儿也是线程共享的,主要存储已经加载的类信息、常量、静态变量等。这个区域在JVM规范中为堆内存的一个逻辑部分,HotSpot虚拟机中这里被称作永久代。当然常量池也包含在方法区。

以上是对JAVA虚拟机内存模型的细分,在实际当中,可以认为JVM的内存模型就可以简单的分为栈、堆和方法区,这三个部分的内存也是经常需要优化的地方。

支持一下
扫一扫,支持forsigner