微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

深入理解Java虚拟机-第2章-JVM内存模型

本章主要是学习下JVM的内存模型,书中有一点讲的非常好:Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。

一、前言

对于使用c和c++等程序开发的开发人员来说,内存的申请与释放的控制权在开发人员手中,这种最高的控制权既有好处也有坏处,

  • 好处是开发人员可以明确控制和度量内存的使用,内存的使用对开发人员是不透明的,是感知的。
  • 不好的一方面是对程序员的要求较高,需要时刻注意内存的申请与释放,不及时的释放容易造成内存泄漏。

Java虚拟机提供了内存的管理机制,管理这内存的申请与释放的时机,内存的使用对程序员是透明的,程序员不用担心内存泄漏问题(除非程序有问题,存在内存申请不释放的问题),极大了解放了开发人员的精力,让开发人员有更多的精力和时间去做其他的事情。
作为一个开发人员,要想更好的了解程序的运行机制,写出优雅的代码,同时在出现内存方面的问题时,能够快速定位问题,需要熟练了解JVM内存的管理机制。

二、内存模型

Java虚拟机在程序运行的过程中会讲内存分为几个区域,分别是:堆、方法区、程序计数器、Java虚拟机栈、本地方法栈。基本内存分布情况如下所示:

在这里插入图片描述

接下来我们将对各个模块进行介绍说明

2.1 堆

堆是存放对象实例和数组的区域,是JVM虚拟集管理的一大块内存,有如下特点:

  • 所有线程共享的区域。
  • 在虚拟机启动的时候创建的,可以通过-Xmx和-Xms控制堆的大小,-Xms表示初始化堆大小,-Xmx是堆的最大值。一般为了防止堆来回 伸缩向操作系统分配内存,提高性能,可以将-Xms和-Xmx设置同样大小。
  • 一般理解对象实例都是放到堆上,但是随着JIT编译器的发展和逃逸分析技术的成熟,栈上分配、标量替换优化技术,有些对象会分配到栈上。也可能分配到线程拥有的TLAB上,TLAB(Thread Local Allocation Buffer)是线程私有的内存区域,可以有效的减少内存竞争引发的性能损耗。可以通过-XX:+UseTLAB设置是否启用TLAB。默认是开启的。
  • GC堆,就是Garbage Collected Heap.
  • 堆中划分为Eden、From Survivor和To Survivor三个区域,比例为8:1:1。不同的区域采用的垃圾收集算法不同。
  • 堆的空间可以是不连续的,只要逻辑上是连续的。
  • 内存不够时会抛出OutMemoryError的异常。

2.2 方法区

方法区是JVM虚拟机模型规定的一块区域:持久代,比如在Hotspot虚拟机上,将持久代和方法区等价的,方法区主要是存储加载的Class信息、常量、静态变量、即时编译器编译后的代码。是各个线程共享的区域。-XX:PermSize设置永久代初始大小。-XX:MaxPermSize设置永久代最大可分配空间。超过最大值出现内存溢出问题。
在JDK1.7开始,方法区的常量池已经挪到堆中,方法区被拆成几部分,class类型些放到了直接内存metasapce中。metaspace是没有内存上限的,可以使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置元空间初始大小以及最大可分配大小。只受限于物理内存大小。
这个区域只要是常量池的回收和对类型的卸载,一般只有Full GC的时候会触发。对应一个64位的服务端JVM来说,其默认的-XX:MetaspaceSize值为21MB,这就是初始的高水位线,一旦元空间的大小触及这个高水位线,就会触发Full GC并会卸载没有用的类,然后高水位线的值将会被重置。如果初始化的高水位线设置过低,会频繁的触发Full GC,高水位线会被多次调整。所以为了避免频繁GC以及调整高水位线,建议将-XX:MetaspaceSize设置为较高的值,而-XX:MaxMetaspaceSize不进行设置。

运行时常量池是方法区的一部分,常量池存储编译期生成的各种字面量和符号引用。运行期间产生的新的常量也可以放到常量池中。

2.3 虚拟机栈

Java虚拟机栈是线程执行过程中需要的资源,是线程私有的。每个方法的执行对应着虚拟机栈的栈帧的进入进出。栈帧中用于存储局部变量表、操作数栈、动态链接、方法出口信息。
局部变量表中保存了基本类型、对象引用和returnAddress信息。
超出堆栈大小抛出StackOverFlowError异常。

2.4 本地方法栈

本地方法栈和虚拟机栈有着类似的结构,是应用调用本地方法(Native 方法)使用的堆栈。有些虚拟机将本地方法栈和虚拟机栈合二为一。

2.5 程序计数器

程序计数器是线程私有的,是记录线程执行的指令地址,线程在使用完cpu时间片的时候,当重新分配到时间片恢复现场时,需要程序计数器指定恢复的现场,程序计数器是整个JVM区域唯一没有溢出的区域。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐