各个内存区的简单描述
程序计数器:当前线程所执行的更高效的行号指示器,通过改变计数器的值,来选取下一条需要执行的字节码指令
虚拟机栈:每个方法在执行的同时都会创建一个栈帧(Stack Framne),每个方法从调用到执行完成,就对应这个一个栈帧的入栈到出栈。
JAVA堆:内存中最大的一块
1:几乎所有的对象都在堆上分配内存
2:又被称为“GC堆”,垃圾收集器的主要管理工具
3:也可能分配出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)
本地方法栈:不同于虚拟机栈是对于Java方法,本地方法栈是针对于Native方法。
方法区:(Non-Heap)非堆,用于区别java堆区
用于存储已被虚拟机加载的类信息,常量,静态变量,即使编译器编译后的代码等数据
对象的创建
对象的创建通常仅仅是一个new关键字
1:检查这个指令的参数能否在常量池中定位到一个类的符号引用
2:检查是否加载,解析,初始化过,没有的话,必须先执行相应的类加载过程
3:接下来为新生对象分配内存
注:new指令之后会接着执行<init>方法
对象的内存布局
1:对象头Header:保存运行时的数据和类型指针
2:实例数据Instance Data
3:对齐填充Padding
对象的访问定位
1:句柄
2:直接指针
内存异常
OutOfMemoryError(OOM):除了程序计数器之外,其他内存都有可能发生
解决流程:
1:使用内存映像分析工具,确定是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)
注:内存泄漏指的是,申请内存之后无法释放内存。
内存溢出指的是,申请内存时,没有足够的空间。
内存泄漏最终会导致内存溢出
2:如果是内存泄漏,使用工具查看泄漏对象到GC Roots的引用链,于是就能分析出这些对象到底是为什么不能被垃圾回收器收集
如果不是泄漏,就表示内存中的对象是都需要存活的,检查虚拟机的堆参数(-Xmx、-Xms),与机器的物理内存对比是否可以调大;从代码上检查,是否存在某些对象生命周期过长、持有状态时间过长。
虚拟机栈和本地方法栈溢出
StackOverflowError 线程请求的栈深度大于虚拟机所允许的最大深度
【实际应用中没有使用内存分析工具,大家有实战的用到的,欢迎补充】