概述
- Java Virtual Machine-java程序的运行环境
- 好处
- 一次编写,到处运行
- 自动内存管理,垃圾回收功能
- 数组下标越界检查
- 多态
- jvm,jre,jdk比较
- 学习路线
内存结构
程序计数器
- 作用:是记住下一条jvm指令的执行地址
- 特点:
- 是线程私有的
- 不会存在内存溢出
- 命令流程:java源代码—>二进制字节码—>解释器—>机器码—>CPU
虚拟机栈
-
每个线程运行时所需要的内存,称为虚拟机栈
-
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
-
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
-
栈帧:每个方法运行时需要的内存
-
问题辨析
- 垃圾回收是否涉及栈内存?
- 否
- 栈内存分配越大越好吗?
- 否,栈内存越大,线程数越小
- 方法内的局部变量是否线程安全?
- 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
- 垃圾回收是否涉及栈内存?
-
栈内存溢出
- 栈帧过多导致栈内存溢出
- 栈帧过大导致栈内存溢出
-
本地方法栈
- 本地方法:不是由java代码编写的代码
堆
- 通过 new 关键字,创建对象都会使用堆内存
- 特点
- 它是线程共享的,堆中对象都需要考虑线程安全的问题
- 有垃圾回收机制
- 内存溢出:OutOfMemoryError
- 内存诊断
- jps:查看当前系统中有哪些 java 进程
- jmap:查看堆内存占用情况 jmap - heap 进程id
- jconsole:图形界面的,多功能的监测工具,可以连续监测
方法区
- 所有java虚拟机线程共享的区域
- 存储了类的结构相关的信息
-
内存溢出
- 1.8 以前会导致永久代内存溢出
- java.lang.OutOfMemoryError: PermGen space
- -XX:MaxPermSize=8m
- 1.8 之后会导致元空间内存溢出
- java.lang.OutOfMemoryError: Metaspace
- -XX:MaxMetaspaceSize=8m
- 1.8 以前会导致永久代内存溢出
-
运行时常量池
- 常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
- 运行时常量池:常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
-
StringTable
- 常量池中的字符串仅是符号,第一次用到时才变为对象
- 利用串池的机制,来避免重复创建字符串对象
- 字符串变量拼接的原理是 StringBuilder
- 字符串常量拼接的原理是编译期优化
- 可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
- 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串 池中的对象返回
- 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回
直接内存
-
常见于 NIO 操作时,用于数据缓冲区
-
分配回收成本较高,但读写性能高
-
不受 JVM 内存回收管理
-
分配和回收原理
- 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
- ByteBuffer的实现类内部,使用了 Cleaner(虚引用)来监测 ByteBuffer对象,一旦 ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过Cleaner的clean方法调用freeMemory来释放直接内存
垃圾回收
如何判断对象可以回收
- 引用计数法
- 可达性分析算法
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以 回收
- 四种引用
- 强引用:只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
- 软引用(SoftReference):仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象,可以配合引用队列来释放软引用自身
- 弱引用(WeakReference):仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象,可以配合引用队列来释放弱引用自身
- 虚引用(PhantomReference):必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队, 由 Reference Handler 线程调用虚引用相关方法释放直接内存
- 终结器引用(FinalReference):无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象
垃圾回收算法
- 标记清除
- 速度较快
- 会造成内存碎片
- 标记整理
- 速度慢
- 没有内存碎片
- 复制
- 不会有内存碎片
- 需要占用双倍内存空间