jvm启动时会在主机相应目录产生一个pid文件,jps就可以通过这个目录查看所有的java进程
jmx(java管理扩展,MBean)可以看作一个特殊的对象,可以通过某种协议访问这个对象,比如rmi、http、snmp
rmi是java rpc的一种实现,远程方法调用
jvm的远程监控原理是rmi,jstatd工具用于建立远程rmi服务器
jvm本地监控的原理是利用进程间共享内存(文件共享),jconsole的MBean标签原理是通过本地协议与jmx通信
jinfo用于实时查看和调整jvm的各项参数,调整的原理应该是通过本地协议访问MBean信息,验证:https://www.jianshu.com/p/fa4e88f95631
jstatd是用于监控远程jvm的一个中间件,jvisualvm可以不用端口号访问远程jvm,要用到jstatd
jconsole必须通过ip和端口号访问远程jvm,远程jvm通过jvm参数开启rmi服务
jps也能连接远程jvm
监控远程jvm两种方法:jvm参数开启rmi服务或者通过jstatd开启rmi服务(jvisualvm,不用端口)
监控本地jvm比如jstat原理是通过共享文件进行
一个问题:怎么通过命令行实时调整远程jvm的参数?jinfo连接远程jvm
类加载器子系统
运行时数据区
1、线程共享:堆、方法区
2、线程私有:java栈(局部变量表、操作数栈、动态链接、方法出口)、本地方法栈、程序计数器
执行引擎(垃圾收集器)
常量池面试题:
1、(a+b)*10,10是存在哪里的?是常量池么?
http://blog.sina.com.cn/s/blog_99201d890102x7mc.html
栈、堆、方法区都会出现OOM
系统原因:
jvm参数设置得不够合理,比如堆内存设置过小
出现原因:
栈:无法增加内存,回出现OOM;(StackOverflowError是栈深度)
堆:内存泄漏导致GC后内存无法回收,无法分配内存给对象,出现OOM;某段时间内出现高并发产生大量对象,无用对象无法及时回收导致OOM
方法区:可以设置jvm参数,当OOM发生时,自动导出堆内存,然后放到MAT类似工具查看哪个对象占用内存过大,然后定位到具体的代码,解决bug
public class StaticFieldTest {
public static StaticFieldTest staticFieldTest=new StaticFieldTest();
public static void main(String[] args) {
new StaticFieldTest();
}
}
new StaticFieldTest();底层->1、new 2、 1是类加载 2是类的构造函数
:1、new 2、 3、putstatic
可以看出上面的new还没完成后面就在被调用了new,jvm需要确保类只被加载一次
一个对象new的过程?
1、底层new字节码 类加载
2、分配内存(对象内存大小在类加载完成后就已经确定)TLAB-本地线程分配缓冲
3、内存分配零值
4、
jvm怎么解决new一个对象出现的并发?
1、new字节码-类加载,确保类只被加载一次
2、分配内存出现的并发
ClassLoader的loadClass(String name)、findClass(String name)、defineClass(byte[] b, int off, int len)方法
loadClass(String name)-双亲委派
findClass(String name)-根据类名获取class的字节数组
defineClass(byte[] b, int off, int len)-根据字节数组生成Class
Tomcat有两个应用,存在相同全类名的class,怎么加载?
jvm内存结构:类加载子系统、运行时数据区、执行引擎(垃圾收集器)
对象内存分配策略:栈上分配->TLAB分配->老年代直接分配->Eden区分配
对象是否存活:引用计数法、可达性分析法(GC Roots、Object类的finalize方法)
垃圾回收算法:标记-清除算法(2次遍历)、复制算法(1次遍历)、标记-整理算法(2次遍历)、分代收集算法(对象存活周期,新生代-复制算法,老年代-标记清除算法或标记整理算法)
垃圾收集器
1、串行 Serial和Serial Old
2、并行 PN、PS和PS old
3、并发:CMS和G1
PS和PN的区别是PS可以控制吞吐量和自适应设置,吞吐量与2个参数有关:
1、-XX:MaxGCPauseMillis
2、-XX:GCTimeRatio
自适应设置与-XX:+UseAdaptiveSizePolicy有关
Serial Old-标记整理算法
CMS-标记清除算法
G1-局部复制算法,整体标记整理算法
CMS:
获取最短回收停顿时间为目标;并发收集器,实现垃圾收集线程与用户线程一起工作;基于标记-清除算法;
四个步骤:初始标记(STW、单线程)、并发标记、重新标记(STW、多线程)、并发清除
G1:
Region;Region的4种类型(E、S、O、H);
四个步骤:初始标记、并发标记、最终标记、筛选回收(STW,根据Region的回收价值和成本进行优先级排序,根据用户期望的GC停顿时间制定回收计划)
垃圾回收过程:YoungGC(Eden区满了不一定发生GC,要看-XX:MaxGCPauseMills,扩大内存)
->MixedGC(老年代的堆占有率达到-XX:InitiatingHeapOccupancyPercent触发,回收所有Y区、部分O区和H区)
->Full GC
CMS
对象优先在Eden区分配,如果Eden区空间不足,就会判断老年代的连续空间是否大于新生代对象总大小或者历次晋升的平均大小,如果大于,进行ygc,否则fgc,
如果Eden+S0区存活对象S1区放不下,就会放一部分到老年代,可能会因为内存碎片或者内存空间不足导致晋升失败,这时候触发FullGC。
Region区域划分:E、S、O、H
H区:大对象分配
步骤:
初始标记(STW)
并发标记
最终标记(STW)
筛选回收(STW):-XX:MaxGCPauseMillis 复制算法 根据每个Region的回收价值和成本
特点:
1、并行与并发(CMS也有)
2、分代收集(独立管理整个堆)
3、空间整合(局部-复制算法,整体-标记整理算法)
4、可预测的停顿(-XX:MaxGCPauseMillis)
参数:
-XX:+UseG1GC:使用G1收集器
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:GCTimeRatio
-XX:MaxGCPauseMillis
http://blog.sina.com.cn/s/blog_56146a210100pzro.html
-XX:TargetSurvivorRatio:
-XX:MaxTenuringThreshold:
-XX:G1NewSizePercent:新生代内存初始占比(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大占比
-Xmx512m:堆最大内存大小
-Xms512m:堆初始内存大小
-Xmn170m:-XX:newSize、-XX:MaxnewSize两个参数的同时配置
-XX:newSize:新生代初始内存大小
-XX:MaxnewSize:新生代最大内存大小
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:PermSize:永久代初始内存大小
-XX:MaxPermSize:永久代最大内存大小
-Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,以前每个线程栈大小为256K。更具应用的线程所需内存大小进行调整。
在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论
-XX:MetaspaceSize:元空间初始内存大小
-XX:MetaspaceSize:元空间最大内存大小过(默认45%)执行新生代和老年代的混合收集(MixedGC)
-XX:G1HeapWastePercent:GC过程中如果空闲Region占整堆内存超过(默认5%)停止GC
-XX:G1MixedGCLiveThresholdPercent:Region中存活对象低于(默认85%)才会回收该Region
-XX:G1MixedGCCountTarget:筛选回收阶段的筛选回收次数,多次筛选回收目的是减少单次回收造成的停顿时间过长
类加载&tomcat类加载原理
内存区域(堆&方法区&程序计数器&虚拟机栈&本地方法栈&堆外内存),有什么?栈帧运行原理?常量池?
执行引擎(字节码->汇编指令,多线程怎么实现?)
GC算法(标记-清除&标记-复制&标记-整理)
GC种类(CMS&G1)
方法区(类型信息&常量&静态变量&JIT即时编译器编译后的代码缓存)
注意Class对象是在堆,可以理解成Class对象有一个指针指向方法区的类型信息
方法区的常量也就是运行时常量池,包括字符串常量池、字面量、符号引用、直接引用
JDK7以前,永久代是方法区的实现,非堆;JDK7,把字符串常量池和静态变量移动到堆;JDK8,元空间是方法区的实现,使用本地内存
方法区的垃圾回收:常量池的回收和类型的卸载,类型卸载三个条件
jps
jstat
jstack
jmap
jhat
jinfo
谈谈gc
对象首先Eden区分配,内存不足,
看老年代连续可用空间是否大于历次晋升对象大小,如果是触发ygc
否则先进行老年代的gc,具体要看不同jvm版本和
垃圾收集器的种类,也可能直接进行fgc,收集新生代,
老年代和方法区
谈谈gc
对象首先Eden区分配,内存不足,看老年代连续
可用空间是否大于历次晋升对象大小,如果是触发ygc
否则先进行老年代的gc,具体要看不同jvm版本和
垃圾收集器的种类,也可能直接进行fgc,收集新生代,
老年代和方法区
谈谈fgc吧?
fgc产生的原因有几个
1.方法区内存不足
2.老年代空间不足(上面说到的Eden区内存不足会去看那个老年代的连续内存是否大于之前晋升对象的总大小,如果小于,则会触发fgc)
3 大对象直接进入老年代,但老年代空间不足
解决方法
1.看程序是否动态创建类,比如使用cglib
2.新生代内存是否设置过小,导致频繁发生ygc
3.看程序是否出现内存泄露,比如使用hashmap作为本地缓存,没及时回收无用对象
谈谈对象的创建?
1.new字节码 看类是否已经加载(加载 链接 初始化)
2.为对象分配内存(逃逸分析栈上分配/tlab/ 指针碰撞/空闲列表+cas)
3 对象实例数据赋默认值
4.执行<init>方法(包含构造代码块,构造函数,属性赋值操作,顺序是属性赋值操作,构造代码块,构造函数)
谈谈方法区?
方法区存储内容
类型信息(class文件字节码,不是Class对象,Class对象在堆,可以理解Class对象有指针指向字节码,方便用户操作)
常量(字符串常量池,字面量)
静态变量
即时编译器编译后的代码缓存
垃圾回收
方法区的垃圾回收主要是对常量池的回收和类型的卸载
类型卸载三个条件
1)类的所有实例已经被回收;
2)加载该类的ClassLoader已经被回收;
3)对应的Class类没有被引用;
谈谈G1?
1 Region (最多2048个Region)
2 E S O H
3 年轻代比例 -XX:G1NewSizePercent
-XX:G1MaxNewSizePercent
4 Region动态变化
5 H区存放大对象
6 gc阶段:初始标记 并发标记 最终标记 筛选回收
7 复制算法 把一个Region的存活对象复制到另一个Region
8 低停顿 -XX:MaxGCPauseMillis 优先回收价值较大的Region(比如现在有1000个Region可以回收),但是用户设置只能停顿200ms,根据计算可以回收800个Region,那么就会选择回收这1000个Region中回收价值较大的Region,体现了贪心策略
9 GC种类 YoungGC MixedGC Full GC
10 调优建议 -XX:MaxGCPauseMills 参数