JVM(Java Virtual Machine )内存管理结构

引入:


思考  对于java程序员来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,而且不容易出现内存泄漏和内存溢出问题。那为什么我们还需要去学习内存?
解答:内存对于程序非常的重要,虽然java程序员不需要去管理内存,但是如果出现了内存泄漏和溢出方面 的问题,为了能够更快捷去排查错误,我们需要了解虚拟机是怎样使用内存的。

注意:
    JVM管理内存,这句话并不是非常的简单:我们在java中从来不会去说java多进程编程,而是多线程编程,为什么呢?原因很简单,JVM本身就是一个进程,java是基于JVM编程的。所以JVM所管理的内存是计算机分配给JVM的内存。我们所聊的内存结构是JVM所管理的内存结构。这一点我们要清楚。
 


java运行时数据区域:
       java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间。

1、程序计数器(Program Counter Register):

       程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。
       由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器之后执行一条线程中的指令,因此,为了线程切换后 能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间的计数器互不影响,独立存储,所以它是线程私有(一个线程就有一个程序计数器)的,我们称这类内存为线程私有的内存。
       如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。

2、虚拟机栈(JVM Stack):

      与程序计数器一样,java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,他的生命周期与线程相同,此反应了java虚拟机栈是不需要进行垃圾回收的。虚拟机
       栈描述的是java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)(栈帧是方法运行期的基本数据结构),用于存储局部变量表,操作栈,动态链接,方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
       局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,需要注意的是,局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。
       虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出OutOfMemoryError(内存溢出)。
    
3、本地方法栈(Native Method Stacks):

        本地方法栈(Native Method Stacks)与虚拟机栈发挥的作用非常的相似,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到到的Native方法服务。在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈合二为一。本地方法栈也是线程私有的。也会抛出StatckOverFlowError(栈溢出)和OutOfMemoryError(内存溢出)异常。

4、Java堆(Heap):

       堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,因此很多时候也被称为"GC堆(Garbage Collected Heap)",堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例以及数组,原则上讲,所有的对象都在堆区上分配内存(但是随着技术的发展,也不是这么
 绝对了)。如果堆中没有内存完成实例分配,并且堆也无法在扩展时,将会抛出OutOfMemoryError异常。

5、方法区(Method Area):

       方法区与java堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量 、编译器即时编译后的代码等数据。虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又有一个别名叫做Non-Heap(非堆),目的就是与Java堆区分开。
       方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。
       在方法区上定义了OutOfMemoryError:PermGen space异常,在内存不足时抛出。
       运行时常量池(Runtime Constant Pool)是方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如String类的intern方法,作用是String维护了一个常量池,如果调用的字符“abc”已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。

6、直接内存(Direct Memory):

        直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存,JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值