浅谈JVM
- 请谈谈你对jvm的理解?java8虚拟机跟之前的变化更新?
- 什么事OOM,什么是栈溢出Stack OverflowError ?怎么分析?
- JVM的常用调优参数有哪些?
- 内存快照如何抓取,怎么分析Dump文件?
- 谈谈jvm中,类加载器你的认识?
双亲委派机制:
从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
栈:8大基本类型+对象引用+实例的方法
8大基本类型:
- char 对应的包装类 Character
- boolean Boolean
- int Integer
- short Short
- double Double
- byte Byte
- float Float
- long Long
对象在内存中实例化的过程:
public class Test5 { int age; String name; public void eat(){ } public static void main(String[] args) throws AWTException { int age_a; String name_a; Test5 test5=new Test5(); test5.age=1; test5.name="hahah"; test5.eat(); } }
在jvm虚拟机中会有三个区域:堆、栈、方法区
代码执行后,类中的成员变量跟成员方法会先进入到方法区中
当执行到main方法时,main方法会被压入栈中
当 执行到 Test5 test5=new Test5() 时 会在栈中创建Test5类的引用 然后将成员变量跟成员方法 放入实例中(堆) 都是成员变量跟成员方法的地址值
然后会给对象进行赋值 test5.age=1; test5.name="hahah"; 根据地址值在堆中找到 new Test5()对象。
方法void() 被执行完毕后 会从栈中弹出(弹栈) 最后main() 方法执行完毕之后也会出栈 (栈遵循先进后出原则)
堆的理解:
堆分为:新生区(伊甸区,幸存1区、幸存0区)+养老区+永久区
垃圾回收(GC)一般存在于“伊甸区 ”,称之为 轻GC “幸存区”是“新生区” 与“养老区” 的过渡区 ,未被轻GC清理的对象存活下来会进入 “养老区” 。当“养老区” 满了后,会进行“重GC”(Full GC)。当堆内存满了之后,会引发OOM(OutOfMermoryError)异常。
在JDK1.8之后 永久存储区改了名字:改为了元空间。
所有的对象都是在伊甸区被new出来的。假设 伊甸区 能存放10个对象 ,当10个对象被new出来后,存满会触发轻GC,GC发现有一个对象还在被引用,另外9个对象属于垃圾,就会将9个对象进行清理,然后一个幸存的对象会进入幸存区。当幸存区也全部
满了之后会进行一次重GC(Full GC) ,再幸存下来的对象就会进入养老区。经过研究,有99%的对象都是临界对象!!
永久区:这个区域常驻内存的。用来存放jdk自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息,
这个区域不存在垃圾回收!关闭jvm虚拟机就会释放这个区域的内存。
一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,知道内存满就会出现OOM
- jdk1.6之后 :永久代,常量池是在方法区
- jdk1.7 :永久代,但是慢慢退化了,去永久代,常量池在堆中
- jdk1.8之后:无永久代,常量池在元空间
元空间:逻辑上存在,物理上不存在
public static void main(String[] args) { //返回虚拟机试图使用的最大内存 long max = Runtime.getRuntime().maxMemory(); //返回jvm的总内存 long total = Runtime.getRuntime().totalMemory(); System.out.println(max/1024/1024); System.out.println(total/1024/1024); }
结论:默认情况下,分配的总内存为电脑内存的1/4 初始化的内存是1/64
可使用:-Xms1024m -Xmx1024m -XX:+PrintGCDetails 指令手动修改默认堆空间
注:-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError 该指令可将OutOfMemoryError 错误生产Dump文件,使用JProfiler工具可对其进行分析从而获取更详细的错误详情。
GC算法:
- 标记清除法
- 标记压缩(优化的标记清除法)
- 复制算法
当一个对象经历了15次GC依旧还存活(默认情况下),也可通过 -XX:MaxTenuringThreshold=5 参数来修改默认次数
由于幸存区的对象在重GC后也会进入养老区,估from区跟to区会互相交换。
缺点:永远都会多出一块空间。
优点:没有内存碎片。 - 引用计数器 :每个对象被引用一次之后会被加1,但是这个过程会消耗资源
总结 :
内存效率:复制算法>标记清除算法>标记压缩算法
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
GC中的分代收集算法就是在不同的区使用不同的算法,算是GC的最优算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?