《深入理解java虚拟机》---内存管理(2)

java和C、C++之间素来有一堵墙,这堵墙就是内存动态分配和垃圾收集技术。java的内存分配如下图

《《深入理解java虚拟机》---内存管理(2)》

程序计数器:当前线程所执行的字节码的行号指示器,是一块虚拟的很小的内存空间,线程私有的

java虚拟机栈:线程私有的,生命周期与线程相同,里面存放的是基本数据类型,对象引用类型(reference类型,但是他们不是对象本身,一般是指向对象起始位置的引用指针,也有可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)等,其中64为长度的long和double类型会占用两个局部空间变量,其余的都是一个,这个区域规定了两种异常情况:如果线程请求的栈深度大于 虚拟机所允许的深度,就会抛出Stack OverflowError异常;如果虚拟机可以动态扩展,在扩展时没有申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈:与虚拟机栈发挥的作用是相似的,不同的是只为本地Native方法服务,抛出异常与虚拟机栈一样,像Sun HotSpot虚拟机会直接把虚拟机栈和本地方法栈合二为一。

java堆:对于大多数应用来说,java堆是java虚拟机中管理内存的最大一块,是所有线程共享的一块区域,在虚拟机启动的时候创建,存放对象实例,也就是说所有的对象实例以及数组都要在堆上分配内存,但是随着JIT编译器的发展和逃逸分析技术的逐渐成熟,未来将会导致一些微妙的变化,所以现在说所有的对象都是在堆上分配内存的就有点绝对了。既然管理着对象实例,那么垃圾回收肯定就是这个区域,所以有的时候这个堆也称为GC堆,为了方便GC线程的回收,java堆中的对象会别分为:新生代和老年代,或者更加细致的划分。在java虚拟机的规范中规定,java堆可以处于物理上不连续的内存空间中,只要逻辑上的连续即可,就像是我们的磁盘空间一样可以是固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是可扩展的(通过配置-Xmx和-Xms参数实现),当堆中没有内存实例分配,并且也无法扩展时,就会抛出OutOfMemoryError异常。

方法区:和堆一样是线程的共享区域,主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。对于HotSpot虚拟机把GC分代收集扩展至方法区,这样更多人愿意将方法区称为“永久代”,所以GC也在管理着方法区,采用永久代实现方法区,但是永久代有一个-XX:MaxPermSize的上限,所以更有可能会导致内存溢出的现象发生,所以后来HotSpot逐渐放弃永久代改成Native Memory来实现方法区。其实对于方法区中回收只是针对常量池的回收和对类型的卸载,对于类型的卸载条件比较苛刻,但是如果为完全回收会导致内存泄漏问题,未来需要进一步的优化,对于常量池的回收,现在JDK1.7的HotSpot中已经把原本的永久代的字符串常量池移出。当方法区无法满足内存分配需求时,就会抛出OutOfMemoryError异常。

运行时常量池:是方法区的一部分,Class文件中除了类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放在编译器生成的各种字面量和符号引用。是方法区的一部分,也受方法区内存的限制,当常量池无法再申请到内存的时候就会抛出OutOfMemoryError异常。

直接内存:主要作用就是为了提高性能,在NIO类中可以直接使用Native函数分配堆外内存供外界调用,既然是内存就会受到本机总内存的限制,可能会出现OutOfMemoryError异常。

 

 

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/hy_coming/article/details/81988768
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞