[TOC]
重要内存区域
方法区
运行时常量池、字段和方法信息、静态变量等数据。
已经加载的类信息、常量、静态变量以及方法代码的内存区域
堆区
Java堆用来存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆存储的对象被垃圾收集器管理,这些受管理的对象无需也无法显式的销毁。
从内存回收的角度,Java堆可以粗略的分为新生代和老年代。从内存分配的角度Java堆中可能划分出多个线程私有的分配缓冲区。不管如何划分,Java堆存储的内容是不变的,进行划分是为了能更快的回收或者分配内存。
对象在堆内存的布局
- 对象头
- MarkWorld,用于存储对象运行时的数据,如hashcode,锁状态标志,GC分代年龄等
- 元数据,指向方法区中目标类的类型信息,通过元数据指针确定对象的具体类型
- 实例数据
- 存储对象中的各种类型的字段信息
- 用于对齐的padding
Java虚拟机栈
每一条Java虚拟机线程都有一个线程私有的Java虚拟机栈(Java Virtual Machine Stacks)。它的生命周期与线程相同,与线程是同时创建的。
存储线程中Java方法调用的状态,包括局部变量、参数、返回值以及运算的中间结果等。一个Java虚拟机栈包含了多个栈帧,一个栈帧用来存储局部变量表、操作数栈、动态链接、方法出口等信息。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java栈中,当该方法执行完成,这个栈帧就从Java栈中弹出。我们平常所说的栈内存(Stack)指的就是Java虚拟机栈。
本地方法栈
支持Native方法,与JVM栈相似
程序计数器
为了保证程序能够连续地执行下去,处理器必须具有某些手段来确定下一条指令的地址,而程序计数器正是起到这种作用。
运行时数据区域
线程隔离
- Program Counter Register 程序计数器
- JVM Stacks 虚拟机栈
- Native Stack
线程共享
- Java Heap 堆
- 是java虚拟机所管理的内存中最大的一块,用来存放对象实例
- 可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可
- 可用过-Xmx和-Xms控制
- Method Area 方法区
- 存储加载过的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 别名为Non-Heap(非堆),是PermentGeneration永久代
- 包含运行时常量池,用于存放各种字面量和符号引用,不要求常量只有编译器才能产生,比如String的intern
类的生命周期
Loading 加载
找到需要加载的类并把类的信息加载到方法区中,然后在堆区中实例化一个Class对象,作为方法区中这个类的信息的入口
- 根据类的全名,生成一份二进制的字节码
- 将字节码解析成方法区对应的数据结构
- 生成Class对象的实例表示该类
Linking 连接
不一定要等到加载完成后,有时可以同时执行(比如Loading和Verification可以同时执行)
Verification
保证类符合Java的语法规范。
比如:bytecode的完整性、final方法的检查、方法签名的检查
Preparation
JVM为类的成员变量分配内存空间并且赋予默认初始值,需要注意的是这个阶段不会执行任何代码,而只是根据变量类型决定初始值。即所有int赋值为0,代码中的初始化赋值不进行运行。
也可能会为有助于提高程序性能的数据结构分配内存,比如method table。
method table这个数据结构包含了指向所有类方法的指针(包括从父类集成的方法)
Resulotion
确认类、接口、属性和方法在类的run-time constant pool的位置,并且把这些符号引用替换为直接引用。
symbolic references ==》 direct reference
Initialization 初始化
如果一个类第一次被直接引用,就会触发类的初始化
在此之前会先触发父类的初始化
过程
会按照代码书写顺序进行初始化,但总体规则是
- static先于非static
- 显式初始化,构造块初始化,最后调用构造函数进行初始化
1 | class Singleton { |
直接引用
- 创建实例(new、反射、cloning、反序列化)
- 调用类的static方法
- 使用或对类、接口的static属性进行赋值(不包括final的与在编译器确定的常量表达式,如果使用的父类的静态变量,则只需要加载父类即可)
- 调用API中的某些反射方法
- 子类被初始化
- 具有main方法的类
被动使用
- 引用父类的静态字段,只会引起父类的初始化,不会引起子类的初始化
- 定义类数组,不会引起类的初始化
- 引用类的常量,不会引起类的初始化
Unloading 卸载
满足下面的情况,类就会被卸载
- 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例
- 加载该类的ClassLoader已经被回收
- 该类对应的java.lang.Class对象没有任何地方被引用,无法再任何地方通过反射访问该类的方法
ClassLoader
程序在启动的时候,并不会一次性加载所有要用的class文件,而是根据程序的需要,通过Java的类加载机制来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其他class文件所引用。所以ClassLoader就是用来动态加载class文件到内存当中的。