转载请注明来源(https://blog.youkuaiyun.com/SingingFisher/article/details/93080148)
内存区域
前言
最近在工作中总是浑浑噩噩,虽然工作的内容基本都能完成,但总觉得缺少什么。昨天在用到StringBuilder时,突然发现已经记不得StringBuilder和StringBuffer以及String三者的区别了,因此决定重新系统学习一下JAVA底层,同时进行博客记录,不再像之前一样,哪里不会百度哪里,看完是记得了,但是缺乏系统的理解,日久之后还是得忘。
希望能够成为更强的自己,也希望能够帮助到其他人。
PS:此篇文档为《深入理解Java虚拟机》第二章的学习总结
1、内存区域划分
不同版本的jdk对内存区域的划分不尽相似,但基本上是一致的,大体上划分为以下几个区域:
1.1、程序计数器(Program Counter Register别名PC/PCR)
1.1.1、特点
- 线程私有
- 空间较小
1.1.2、功能
- 字节码解释器通过改变计数器的值来选择下一条需要执行的字节码指定
- 分支、循环、跳转、异常处理、线程恢复
- 线程执行Java方法时,PCR记录了线程正在执行的字节码指令地址。
- 线程执行本地Native方法(C/C++方法)时,PCR值为NULL
1.1.3、异常
- 没有规定任何OutOfMemoryError的区域
1.2、虚拟机栈(VM Stack,即栈内存,平常所称的 栈 通常是指栈内存中的局部变量表部分)
1.2.1、特点
- 线程私有,生命周期和线程相同
- 每个方法在执行时,都会创建一个栈帧(而不是栈)存放方法内的局部变量表、操作数栈、动态链接和方法出口等信息
- 进入方法时,其在栈帧中需要多少局部变量空间是已经确定的
1.2.2、局部变量表
存放了编译期可知的各种基本数据类型、对象的引用(而不是对象本身,对象本身放在堆内存中)、returnAddress(返回地址,是一条字节码指令的地址)类型
1.2.3、异常
- 线程请求的栈深度大于虚拟机所允许的深度时,抛出StackOverflowError
- 虚拟机栈动态拓展时无法申请到足够的内存,抛出OutOfMemoryError
1.3、本地方法栈(Native Method Stack)
和虚拟机栈很相似,只不过放的是调用本地Native方法时需要保存的数据
1.4、堆内存(Heap)
是JVM管理的内存中最大的一块
1.4.1、特点
- 可拓展(通过-Xms(初始Java堆大小) 和-Xmx(最大堆大小)控制)
- 可以在物理内存上不连续
- 所有线程共享
- GC的主要区域
- 在线程共享的内存之外,可划分出多个线程私有的分配缓冲区(Thead Local Allocation Buffer/TLAB)
1.4.2、功能
- 为几乎所有对象实例分配内存
1.4.3、异常
- OutOfMemoryError
1.5、方法区(Method Area)
1.5.1、特点
- 所有线程共享
- 逻辑上归属于堆
1.5.2、功能
- 存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
1.5.3、异常
- OutOfMemoryError
1.5.4、运行时常量池(Runtime Constant Pool)
- 归属于方法区
- 存放编译期生成的各种字面量和符号引用,这些内容将在类加载后进入方法区的运行时常量池中存放
- 抛出OutOfMemoryError
1.6、直接内存(Direct Memory)
NIO等直接使用的物理内存(实际内存分配在物理内存上,堆中给DirectByteBuffer对象分配物理内存的引用,避免了Java堆和Native堆来回复制数据),可能抛出OutOfMemoryError
2、对象创建流程
- JVM遇到new指令时,在常量池中定位到类的符号引用,并且检查是否已经被加载、解析和初始化。如果没有,则加载类
- 类加载检查完毕后,为新对象分配内存
- 将分配到的内存空间全部初始化为零值
- 为对象进行必要的设置(元数据信息、hash码等)
- 执行init方法,初始化变量