目录
JVM
jvm分区
Heap (堆区):主要存储new出来的对象实例,Java堆中细分为:新生代和老年代,一个新生代分为1个Eden区和2个Survivor区,说明:绝大部分对象在Eden区生成,当Eden区装填满的时候,会触发Young Garbage Collection,即YGC。垃圾回收的时候,在Eden区实现清除策略,没有被引用的对象则直接回收。依然存活的对象会被移送到Survivor区。Survivor区分为so和s1两块内存空间。每次YGC的时候,它们将存活的对象复制到未使用的那块空间,然后将当前正在使用的空间完全清除,交换两块空间的使用状态。如果YGC要移送的对象大于Survivor区容量的上限,则直接移交给老年代
元空间区:jdk1.7的方法区移到了元空间,比如类元信息、字段、静态属性、方法、常量等都移动到元空间区,元空间并不在虚拟机中,而是使用本地内存
栈:栈里面存的都是一些局部变量,比如8大基本数量类型,还有线程运行,方法运行都在栈里面,另外创建对象的时候的引用也是存在栈里面的
程序计数器:是一块较小的内存空间。是线程私有的。它可以看作是当前线程所执行的字节码的行号指示器器
类加载
类加载器有这几个:
启动类加载器:jvm启动的时候,会优先加载<JAVA_HOME>\lib这个目录的核心类库。
扩展类加载器:负责加载<JAVA_HOME>\lib\ext这个目录的类。
应用程序类加载器:负责加载我们写的代码。
自定义类加载器:根据我们的需要,加载特定的类。
下图展示了类加载器直接的层次关系,成为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。
它的工作过程是这样的:
- 应用程序类加载器收到了类的加载请求,先问扩展类加载器是否可以加载。
- 扩展类加载器也不会直接去加载,他也是向上级启动类加载器询问是否可以加载。
- 启动类加载器在自己负责的目录搜索了一下,发现自己找不到这个类,就说不行,你自己加载吧。
- 扩展类加载器在自己负责的目录搜索了一下,发现自己找不到这个类,就说不行,你自己加载吧。
- 应用程序类加载器在自己负责的目录搜索了一下,找到了这个类,把Hello类加载进来。
双亲委派模型一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。
双亲委派存在得意义是什么?
保证系统的安全
垃圾回收器有哪些?
串行垃圾回收器(Serial):它为单线程环境设计并且只使用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境。
并行垃圾回收器(Parallel):多个垃圾回收线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理等弱交互场景。jdk8默认的是使用的Parallel并行回收器
并发垃圾回收器(CMS):用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程。互联网公司多用它,适用于对响应时间有要求的场景。
垃圾回收算法?
标记清除:
复制算法:
标记整理:
分代收集算法:
JVM调优的几种场景
CPU占用过高
CPU过高的原因一般是,死循环,递归,计算量大,线程数过多,怎么确定CPU飙升的问题如下:
用top命令查看cpu占用情况
用top -Hp命令查看线程的情况
可以看到是线程id为7287这个线程一直在占用cpu,把线程号转换为16进制,用jstack工具查看线程栈情况
内存溢出解决
程序发生内存泄漏后,进程的可用内存会慢慢变少,最后的结果就是抛出OOM错误。发生OOM错误后可能会想到是内存不够大,于是把-Xmx参数调大,然后重启应用。这么做的结果就是,过了一段时间后,OOM依然会出现。最后无法再调大最大堆内存了,结果就是只能每隔一段时间重启一下应用
用jstat分析gc活动情况
jstat是一个统计java进程内存使用情况和gc活动的工具,参数可以有很多,可以通过jstat -help查看所有参数以及含义
用jmap工具dump出内存快照