JVM 小结
“Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。”
java与c++区别:
- java的 类 单继承,c++多继承
- java有垃圾回收机制,不需要程序员手动释放无用内存。
- java不提供指针,更安全
- c++支持操作符重载,java不支持操作符重载,+ += 仅对String重载。
内存结构
程序计数器
- 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地
址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。此内存区域是唯
一一个在《Java虚拟机规范》中没有规定任何OutOfM emoryError情况的区域。
虚拟机栈
本地方法栈
堆
方法区
存放已经被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 不需要连续内存,可以动态扩展
- 对方法区进行垃圾回收主要目标是对
常量池的回收
和对类的卸载
- 运行时常量池是方法区的一部分
- 方法区属于虚拟机实现细节,不受《Java虚拟机规范》管束,并不要求统一
运行时常量池
Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
永久代和元空间
- 永久代: HotSpot虚拟机选择把收集器的分代设计扩展至方法区,使用永久代来实现方法区,HotSpot的垃圾收集器能够像管理Java堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。
- 永久代实习方法区的设计导致了Java应用更容易遇到内存溢出,因为:永久代有-XX:M axPermSize的上限,即使不设置也有默认大小。
- JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了JDK 8,终于完全废弃了永久代的概念,改用元空间实现。
垃圾收集
判断一个对象是否可被回收
- 引用计数
可达性分析
可达性分析
- GC Roots 起点搜索。
- GC Roots 一般包含:虚拟机栈中局部变量表中引用的对象,本地方法栈中 JNI 中引用的对象,方法区中类静态属性引用的对象,方法区中的常量引用的对象。
finalize()
类加载机制
类在运行期间第一次使用时动态加载,而不是一次性加载所有类。
类的生命周期
类加载:
- 加载 : 类的完全限定名称 -> 类的二进制字节流 ;字节流表示的静态存储结构转换为方法区的运行时存储结构;存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。
- 验证 : 确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 准备 : 准备阶段为类变量(static)分配内存并设置初始值(一般为0),使用的是方法区的内存。
- 解析 : 将常量池的
符号引用
替换为直接引用
的过程。 - 初始化 : 初始化阶段才真正开始执行类中定义的 Java 程序代码;在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。
使用
卸载
两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。
类加载器
- 启动类加载器(Bootstrap ClassLoader), c++
- 所有其它类的加载器,使用 Java 实现
Java 开发人员 角度
- 启动类加载器
<JRE_HOME>\lib
- 扩展类加载器
<JAVA_HOME>/lib/ext
- 应用程序类加载器 它负责加载用户类路径(ClassPath)上所指定的类库