jvm要点
2024-03-18 15:39:23 29 举报
AI智能生成
JVM基础 JVM类加载机制 JVM垃圾回收
作者其他创作
大纲/内容
类加载机制
类装载方式
隐式装载
new
显式装载
class.forname()
类加载器
启动类加载器(Bootstrap ClassLoader)
java核心类库
Java_HOME/lib/
扩展类加载器(extensions class loader)
Java 的扩展库
\lib\ext
系统类加载器(system class loader)
类路径(CLASSPATH)
用户自定义类加载器
继承 java.lang.ClassLoader类的方式
双亲委派模型
首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成
所有的加载请求都会被传送到顶层的启动类加载器中
父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
JVM 调优
jconsole
jvisualvm
-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
垃圾回收
内存泄漏
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露
尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收
垃圾回收线程
由虚拟机自行执行
在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
没有提供释放已分配内存的显示操作方法
引用类型
强引用:发生 gc 的时候不会被回收。
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。
判断对象是否可以被回收
引用计数器法
不能解决循环引用
可达性分析算法
从 GC Roots 开始向下搜索
永久代
垃圾回收不会发生在永久代
如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)
Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区
垃圾回收算法
标记-清除算法:标记无用对象,然后进行清除回收。
缺点:效率不高,无法清除垃圾碎片。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。
缺点:内存使用率不高,只有原来的一半。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,
一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
一般包括年轻代、老年代 和 永久代
垃圾回收器
新生代 复制
效率高,缺点是内存利用率低
Serial收集器(复制算法)
单线程
ParNew收集器 (复制算法):
多线程
Parallel Scavenge收集器 (复制算法)
追求高吞吐量,高效利用 CPU
老年代 标记整理
Serial Old收集器 (标记-整理算法)
单线程
Serial收集器的老年代版本
Parallel Old收集器 (标记-整理算法)
多线程
Parallel Scavenge收集器的老年代版本
CMS(Concurrent Mark Sweep)收集器(标记-清除算法)
高并发、低停顿,追求最短GC回收停顿时间
G1(Garbage First)收集器 (标记-整理算法)
多线程
整个Java堆(包括新生代,老年代)
分代垃圾回收器
新生代1/3
复制算法
Eden、To Survivor、From Survivor 8:1:1
把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
Minor GC
大对象直接进入老年代
发生大量的内存复制
长期存活对象将进入老年代
当年龄达到一定程度(默认 15) 就会被晋升到老年代
老年代2/3
到达某个值之后,全局垃圾收回
标记整理
Major GC/Full GC
主要组成
两个子系统
Class loader(类装载)
xecution engine(执行引擎)
即时编译器
垃圾回收期
两个组件
Runtime data area(运行时数据区)
方法区
类信息、常量、静态变量、即时编译后的代码
静态变量放在方法区
静态的对象还是放在堆。
堆
所有线程共享
几乎所有的对象实例都在这里分配内存
栈
虚拟机栈
用于存储局部变量表、操作数栈、动态链接、方法出口等信息
本地方法栈
为虚拟机调用 Native 方法服务
程序计数器
所执行的字节码的行号指示器
Native Interface(本地接口)
类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,
而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,
而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能
首先利用IDE集成开发工具编写Java源代码,源文件的后缀为.java;
再利用编译器(javac命令)将源代码编译成字节码文件,字节码文件的后缀名为.class;
运行字节码的工作是由解释器(java命令)来完成的。
基础
堆栈
堆
物理地址分配对对象是不连续,性能慢
GC的时候也要考虑到不连续的分配
标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)
运行期确认的,因此大小不固定
实例和数组,数据的存储
栈
先进后出的原则,物理地址分配是连续的。所以性能快
编译期就确认,大小是固定的
局部变量,操作数栈,返回结果,程序方法的执行
对象的创建
使用new关键字 调用了构造函数
使用Class的newInstance方法 调用了构造函数
使用Constructor类的newInstance方法 调用了构造函数
使用clone方法 没有调用构造函数
使用反序列化 没有调用构造函数
分配内存
指针碰撞
规整,即所有用过的内存放在一边,而空闲的的放在另一边
将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离
空闲列表
不是规整的,由虚拟机维护一个列表来记录那些内存是可用的
从列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。
并发安全问题
同步处理
CAS + 失败重试
本地线程分配缓冲
每个线程在 Java 堆中预先分配一小块内存
只有 TLAB 用完并分配新的 TLAB 时,才需要同步锁
对象的访问定位
句柄访问
句柄池
引用中存储对象的句柄地址
句柄中包含了 对象实例数据 与 对象类型数据 各自的具体地址信息
引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改
直接指针
引用 中存储的直接就是对象地址,
对象中存储类型数据
速度更快,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。
0 条评论
下一页