JVM偏理论,主要靠背 面试题
JVM的位置
JVM在操作系统之上,和其他的应用软件层级并列,在之上可以跑java程序
.java => class文件 => 类加载器 Class Loader =>运行时数据区
JVM 总体系结构
类加载器 ClassLoader
类加载器细分:
1. bootStrap - 根 类加载器(jre/ lib/ rt.jar)
2. ext - 扩展加载器 (jre/ lib/ ext/ ...)
3. app - 应用程序(系统类) 加载器,最常用
双亲委派机制
选择类加载器的顺序:
bootStrap->ext->app。先找boot,再找ext,最后找app也就是自己写的。都找不到就报错Class Not Found
好处:
防止用户乱定义类:比如自定义一个String类,这时会先从BootStrap找,所以不会轮到自定义的String类
沙箱安全机制
Java安全模型的核心是 沙箱 sandBox
组成沙箱的基本组件:
字节码校验器(bytecode verifier): 确保遵循java语言规范,帮助实现内存保护;核心类不参与校验;
类加载器:双亲委派机制 + 将代码归入保护域
Native
例子:
private native void start0(); //java的范围达不到了,要调用C++的库了 //会进入本地方法栈,然后调用本地方法接口JNI(Java Native Interface)
进入本地方法栈,然后调用本地方法接口JNI(Java Native Interface)
JNI的作用:扩展Java的使用,一般用于驱动硬件
三种JVM
Sun公司:HotSpot、OpenJDK
其他:JRockit、J9VM
程序计数器PC
每一个线程私有的,指向下一条指令的地址
PC寄存器:仅存一个指针,占用空间非常小
方法区(内含常量池)
方法区被所有线程共享,
方法区含有:静态变量static、常量final、类信息(构造方法、接口定义)、运行时的常量池
=> static + final + Class + 常量池
(堆:实例变量)
栈
栈内存的生命周期 和线程同步
线程结束,栈内存就释放了;所以对于栈来说,不存在GC垃圾回收
栈溢出:StackOverflowError 是比较严重的错误
堆
堆内存的大小是可以调节的
堆中的内容:
实例对象中的 方法、常量、变量
堆内存细分为3个区域:
新生区 young
老年区 old
永久区 perm
新生区-伊甸园 ,经过轻GC,到幸存区,再经过重GC,到老年区;
层层筛选后,等老年区也满了,就会OOM内存溢出
经验:99%的对象都是临时对象。
新生区
老年区
永久区(元空间)
元空间,常驻内存。关闭JVM会释放这个区域的内存。
用来存放JDK自身携带的Class对象,被所有的线程、对象共享;
Interface元数据,存储的是Java运行时的一些环境or类信息,这个区域不存在垃圾回收
永久区OOM:
Tomcat部署了太多的应用;
大量动态生成的反射类
堆内存调优
public class Test { public static void main(String[] args) { long max = Runtime.getRuntime().maxMemory(); System.out.println("max = " + max/1024/1024 + "MB"); long total = Runtime.getRuntime().totalMemory(); System.out.println("total = " + total/1024/1024 + "MB"); //默认max=1/4的总内存; total=1/64的总内存 } }
Configuration里面的VM Options设置为:
-xms1024m -Xmx1024m -XX:+PrintGCDetails
制造OOM:
-xms2m -Xmx2m -XX:+PrintGCDetails ##设置堆的大小为2m
public class Test { public static void main(String[] args) { long max = Runtime.getRuntime().maxMemory(); System.out.println("max = " + max/(double)1024/1024 + "MB"); long total = Runtime.getRuntime().totalMemory(); System.out.println("total = " + total/(double)1024/1024 + "MB"); //默认max=1/4的总内存; total=1/64的总内存 String str = "123"; while(true){ str+="111"; } } }
解决OOM:
1)扩大堆内存,试下能不能解决
2)内存快照分析工具
jprofiler 作用:
设置dump导出错误信息:
-xms1m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
public class Test { public static void main(String[] args) { long max = Runtime.getRuntime().maxMemory(); System.out.println("max = " + max/(double)1024/1024 + "MB"); long total = Runtime.getRuntime().totalMemory(); System.out.println("total = " + total/(double)1024/1024 + "MB"); //默认max=1/4的总内存; total=1/64的总内存 byte[] array = new byte[1*1024*1024]; //1M ArrayList<Test>list = new ArrayList<Test>(); int count = 0; try { while(true){ list.add(new test()); //问题所在 count++; } } catch (Error e) { System.out.println("count = " + count); e.printstacktrace(); } } }
执行上述程序,生成:Dumping heap to java_pid3044.hprof
1)双击打开,查看
2)点击
GC:
GC 4个常用算法
1)引用计数法:
缺点:计数器本身损耗很多性能,效率不高
2)复制算法:
==》适用于复制率较低的 新生区(含:伊甸园区+2个幸存区)
两个幸存区轮流倒,from=>to,谁空谁是to
每次GC后,Eden区、from-幸存区 都是被倒空的
当1个对象经历15次GC,都还没有死,就会荣升 老年区
优点:没有内存的碎片,速度快
缺点: to区永远是空的,浪费了不少内存空间
3)标记清除法(两轮)
flag标记是否存活,一次扫描是否存活、一次扫描清除死的对象
缺点:两次扫描浪费时间;内存碎片
优点:不需要额外的空间(相对于复制算法)
4)标记清除压缩法(三轮)
防止内存碎片:增加一轮扫描,将对象移到一端;
@H_237_404@
GC算法总结:(下面均是越左越好)
GC:分代收集算法
年轻代:
存活率低、复制率低
=> 复制算法
老年代:
区域大,存活率高
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。