JVM笔记
一、内存结构
执行过程
- 类放在方法区 Method Area中
- 类创建的实例(对象)放在堆Heap中
- 堆里的对象调用方法时,会用到虚拟机栈 JVM Stacks,程序计数器 PC Register和本地方法栈 Native Method Stacks
- 每行代码由解释器Interpreter逐行执行
- 热点代码由即时编译器 JIT Compiler执行
- GC模块 垃圾回收模块会对堆中不再使用的实例进行回收
- java代码不方便实现的功能,必须调用底层操作系统的功能,就需要调用本地方法接口
1、程序计数器
- 作用:记住下一条jvm指令的执行地址
- 特点
- 线程私有的 (每个线程都有自己的程序计数器,随着线程创建而创建,随着线程销毁而销毁)
- 不会存在内存溢出
- 是一块较小的内存空间
2、虚拟机栈
2.1 定义
栈-线程运行需要的内存空间,每个线程都需要一个栈,多个线程运行就会有多个虚拟机栈
- 一个栈由多个栈帧组成
- 一个栈帧对应着一次方法的调用
栈帧-每个方法运行时需要的内存
如果方法一调用方法二,方法二会生成一个栈帧,压入栈中,方法运行完毕,弹出栈。
问题辨析
- 垃圾回收是否涉及栈内存?
垃圾回收涉及堆,并不涉及栈内存 - 栈内存分配越大越好吗?
栈内存分配越大,线程数会越少,所以不是栈内存分配越大,程序运行越快 ,因为总共的物理内存是一定的。 - 方法内的局部变量是否线程安全?
线程安全,当线程1运行一个方法,定义一个局部变量,进行操作,同时,线程2也调用运行同一个方法,会在线程2 中再定义一个局部变量,互不影响,所以方法内的局部变量是线程安全的。- 局部变量是线程私有的。
- 全局变量是线程共有的,是需要考虑线程安全问题(不是线程安全的)。
- 当变量是参数,或者是返回值的时候==(逃离了线程的作用范围)==也是需要考虑线程安全问题(不是线程安全的)。
2.2 栈内存溢出 StackOverflowError
- 栈帧过多导致栈内存溢出。(例如不正确的递归调用,递归时没有正确结束)
- 栈帧占用内存过大。(不太容易出现)
2.3 线程运行诊断
- 案例1:CPU占用过多(while死循环)
- 案例2:程序运行很长时间没有结束(发生死锁)
3、本地方法栈 Native Method Stacks
本地方法(Native)使用的内存空间-本地方法栈
有一些java代码不方便实现的功能,必须调用底层操作系统的功能,就需要调用本地方法接口的本地方法
4、堆
4.1定义
- 通过new关键词,创建的对象都会使用堆内存
特点 - 它是线程共享的,堆中的对象都需要考虑线程安全的问题
- 有垃圾回收机制
4.2 堆内存溢出 OutOfMemoryError
创建的对象不能被回收,并且仍在扩大对象所占用的内存。
4.3堆内存诊断
1.jps工具
- 查看当前系统有哪些java进程
2.jmap工具
- 查看堆内存占用情况 jmap -heap $id
3.jconsole工具
- 图形界面的,多功能的检测工具,可以连续监测
5、方法区
5.1定义
方法区是所有jvm虚拟机共享的区;它存储了跟类的结构相关的信息,有成员变量,方法数据和成员方法以及构造方法的代码部分,包括特殊的方法(类的构造器)
方法区在虚拟机启动时被创建,逻辑上是堆的组成部分
5.2 组成
在1.6被称为永久代
在1.8之后也是一种概念,永久代被替代,由元空间实现,包括类,类加载器和常量池,不再占用堆内存,不是由jvm管理内存结构,由本地内存管理。
5.3方法区内存溢出
- 1.8以前会导致永久代内存溢出