赞
踩
class十六进制码的顺序就是按下面的顺序排列的,解析的时候也是按照这个顺序解析
占用两个字节,access_flags的值和下面的每一项进行与操作,对应的二进制位上为1则表示该位为真。
先参数列表后返回值,参数放在小括号内, (参数)返回值。
void m() -> ()V
String toString() -> ()Ljava/lang/String
int add(int a, int b) -> (II)I
类,方法表, 字段表,分别表示这个属性会在类文件、methods、fields中出现。
hello.java -> javac -> hello.class
从底向上询问类加载器是否已经加载了某个class,如果没有加载则继续向上询问父加载器,父加载器通过getParent()方法获得,如果询问到BootStrap类加载器都没有加载到这个class,则从上向下委派加载。每个classLoader内部都有一儿缓存,缓存加载过的类。
为什么需要双亲委派机制?主要是因为安全性,加载加载java.lang.String类,如果不向上面的类加载器询问,而是下面的类加载器直接加载,那么jdk中的类就可以被我们定义的类覆盖了。
AppClassLoader类加载器加载class的位置
ExtClassLoader类加载器加载class的位置
BootStrapClassLoader类加载器加载class的位置
String pathBoot = System.getProperty("sun.boot.class.path"); System.out.println(pathBoot.replaceAll(":", System.lineSeparator())); System.out.println("--------------------"); String pathExt = System.getProperty("java.ext.dirs"); System.out.println(pathExt.replaceAll(":", System.lineSeparator())); System.out.println("--------------------"); String pathApp = System.getProperty("java.class.path"); System.out.println(pathApp.replaceAll(":", System.lineSeparator())); //运行结果 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=55565:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/tools.jar:/Users/lufei/development/git_repository/JVM-master/out/production/JVM com.mashibing.jvm.c2_classloader.T003_ClassLoaderScope /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/resources.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/sunrsasign.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jsse.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jce.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/charsets.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jfr.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/classes -------------------- /Users/lufei/Library/Java/Extensions /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext /Library/Java/Extensions /Network/Library/Java/Extensions /System/Library/Java/Extensions /usr/lib/java -------------------- /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/charsets.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/deploy.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/cldrdata.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/dnsns.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/jaccess.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/jfxrt.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/localedata.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/nashorn.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunec.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext/zipfs.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/javaws.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jce.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jfr.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jfxswt.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jsse.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/management-agent.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/plugin.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/resources.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ant-javafx.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/dt.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/javafx-mx.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/jconsole.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/packager.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/sa-jdi.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/tools.jar /Users/lufei/development/git_repository/JVM-master/out/production/JVM /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
双亲委派中的概念,加载器的父加载器。
//加载器是BootStrap System.out.println(String.class.getClassLoader()); //加载器是BootStrap System.out.println(sun.awt.HKSCS.class.getClassLoader()); //加载器是Ext System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader()); //加载器是App System.out.println(T002_ClassLoaderLevel.class.getClassLoader()); //加载App和Ext类加载器的类加载器都是BootStrap System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getClass().getClassLoader()); System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader()); //自定义类加载器的父加载器是App System.out.println(new T006_MSBClassLoader().getParent()); System.out.println(ClassLoader.getSystemClassLoader()); //运行结果 null null sun.misc.Launcher$ExtClassLoader@5e2de80c sun.misc.Launcher$AppClassLoader@18b4aac2 null null sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$AppClassLoader@18b4aac2
使用场景,加密源码,使用自定义类加载器解密
测试代码
//调用classLoader的loadClass()方法
Class clazz = T005_LoadClassByHand.class.getClassLoader().loadClass("com.mashibing.jvm.c2_classloader.T002_ClassLoaderLevel");
System.out.println(clazz.getName());
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { //如果自己没有加载过,则委托父加载器递归调用这个方法,递归结束的条件是父加载器加载过这个此class或者父加载器是BootStrap。 if (parent != null) { c = parent.loadClass(name, false); } else { //如果当前是BootStrap加载器,则停止递归,直接加载class,但是可能加载不到此class,返回null c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } //如果父加载器没有加载到此class,则调用自己覆写父类的findClass(name)方法 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //子类覆写这个方法 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
通过分析源码,自定义类加载器只需要继承ClassLoader类,然后覆写findClass()方法即可。
public class T006_MSBClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { File f = new File("c:/test/", name.replace(".", "/").concat(".class")); try { FileInputStream fis = new FileInputStream(f); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int b = 0; while ((b=fis.read()) !=0) { baos.write(b); } byte[] bytes = baos.toByteArray(); baos.close(); fis.close();//可以写的更加严谨 return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); //throws ClassNotFoundException } }
自定义类加载器直接覆写loadClass()方法
JVM会为每一个class或interface创建运行时常量池。
运行时常量池中的东西主要是从class或interface二进制文件中的constant_pool中提取出各种类型的符号引用,存到内存中以便程序运行时使用。有以下类型的符号引用:
类加载过程就是指类加载器加载class文件的过程。
加载,连接, 验证,准备,初始化这五个阶段的会按顺序开始,注意只是按顺序开始,并不是按顺序完成或结束。一个阶段执行的过程中可能会交叉执行另一个阶段的执行。解析阶段在某些情况下可以在初始化阶段之后再开始,这是为了支持java语言的动态绑定。
初始化过程其实就是执行方法
加载阶段,虚拟机需要完成以下三件事情:
jvm要确保在有多个类加载器的的情况下进行class的安全linking。虽然同一个符号引用N,被不同的类加载器加载的class有可能是不同class。
但是当在类C = <N1, L1> 中,(N1表示C的全类名,L1表示C的类加载器)有一个对类D = <N2, L2>中的field或method的符号引用,这个符号引用包含了field或者method的返回值或参数类型的descriptor,那么在filed或method的descriptor中出现的包含此field或method的class或interface的符号引用N,必须保证不管是用L1加载还是用L2加载都是同一个class或同一个interface。在准备和解析阶段会执行这个校验。
linking一个class或interface包含验证和准备这个class或interface,以及它的父类,它的父接口。符号引用的解析是可选的,不一定在linking阶段发生。
jvm规范并没有规定linking阶段什么时候发生,只要满足以下条件即可:
验证可能会load别的class或interface,但是不会需要验证和准备别的class或interface。
验证阶段大致上会完成下面4个阶段的检验动作: 文件格式验证,元数据验证,字节码验证,符号引用验证。
准备阶段是正式为类变量分配内存并设置类初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。准备阶段必须在初始化之前结束。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用在Class文件中它以CONSTANT_Class_info, fieldref_info,methodref_info等类型的常量出现。
虚拟机规范中并未规定解析阶段发生的具体时间,只要求了在执行这16个用于操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析。所以虚拟机实现可以根据需要来判断是在类加载器加载时就对常量池中的符号引用进行解析,还是等到一个符号引用将要被使用前才去解析它。
具体解析过程参考链接解析过程描述
假设当前类是D,要解析的field的符号引用中指定的class或interface是C。
要在一个class D中解析class C的method,需要先解析C的符号引用,这一步参考Class and Interface Resolution。解析方法的步骤如下:
要在一个class D中解析interface C的method,需要先解析C的符号引用,这一步参考Class and Interface Resolution。解析方法的步骤如下:
对method type和method handle的作用还不清楚,忽略。
对Call Site Specifier的作用还不清楚,忽略
在执行invokeinterface指令或invokevirtual指令时,会根据当前操作数栈中的对象和invokeinterface或invokevirtual指令之前解析的method对象,确定执行哪个method,假设操作数栈中的对象的类型是C, C是一个class或interface,之前指令解析的method是mR,确定执行哪个class中的方法的规则如下:
实际代码执行时,可能变量声明的类型是C的父类D,而实际的对象也就是操作数栈中的对象的类型是C,那么虽然method_info中class_info指向的是D,但是解析具体要执行的方法时应该先找C中有没有这个方法,这就是多态。
当security manager允许exit或halt操作时,调用Runtime或System的exit方法,或者调用Runtime的halt方法时,就会停止jvm。
思路:写一个javaagent获取对象大小。
新建项目ObjectSize(1.8)
创建文件ObjectSizeAgent
package com.mashibing.jvm.agent;
import java.lang.instrument.Instrumentation;
public class ObjectSizeAgent {
private static Instrumentation inst;
public static void premain(String agentArgs, Instrumentation _inst) {
inst = _inst;
}
public static long sizeOf(Object o) {
return inst.getObjectSize(o);
}
}
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent
注意Premain-Class这行必须是新的一行(回车 + 换行),确认idea不能有任何错误提示
打包jar文件
在需要使用该Agent Jar的项目中引入该Jar包
project structure - project settings - library 添加该jar包
运行时需要该Agent Jar的类,加入参数:
-javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar
package com.mashibing.jvm.c3_jmm; import com.mashibing.jvm.agent.ObjectSizeAgent; public class T03_SizeOfAnObject { public static void main(String[] args) { System.out.println(ObjectSizeAgent.sizeOf(new Object()));// 8 + 4 + padding = 16个字节 System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));// 8 + 4 + 4 + padding = 16个字节 System.out.println(ObjectSizeAgent.sizeOf(new P()));// 8 + 4 + (4 + 4 + 4 + 1 + 1 + 4 + 1) + padding = 32 } private static class P { //8 _markword //4 _oop指针 int id; //4 String name; //4 int age; //4 byte b1; //1 byte b2; //1 Object o; //4 byte b3; //1 } }
下面是32位的结构的例子, 64位markword的例子参考下Hotspot源码。
对象的hashcode是调用未覆写的hashCode()方法或者System.identityHashCode()方法时才会生成的,如果对象是无锁态,生成的hashCode会放在markword中,如果是轻量级锁,重量级锁状态则放在对象的monitor中,如果是偏向锁状态,markword没有地方存hashCode所以锁必须升级,升级成轻量级锁状态。
插桩一般指的是获取计算机软件或者硬件状态的数据的技术。
这个类提供了插桩java代码的方法,Instrumentation的目的是为了帮助一些工具能够收集数据,这些工具的例子: 监控代理, 分析工具, 代码覆盖率分析, event loggers。
有两种方式可以获取一个Instrumentation的实例:
一个agent会提供这个接口的实现,用于transform class。transformation发生在classLoader defined class之前。
transform方法执行时classfileBuffer入参是从哪来的
有以下三种情况:
解释器, bytecode intepreter
JIT, Just In-Time compiler, 热点代码编译
混合模式(-Xmixed): 混合使用解释器 + JIT, 起始节点采用解释执行,然后随着程序的执行进行热点代码检测,把热点代码编译成本地cpu直接可以执行的汇编指令。热点代码是指多次被调用的方法(方法计数器,检测方法执行频率), 多次被调用的循环(循环计数器: 检测循环执行的频率)。启动速度较快。
-Xint, 解释模式,启动很快,执行稍慢
-Xcomp, 使用纯编译模式,执行很快,启动很慢
每个线程都有自己的pc register, 任何时刻一个线程都只会执行某一个方法,如果这个方法不是native的,则pc register包含了当前正在执行的jvm 指令。pc register足够大可以保存一个return address。比如这个方法体执行完时,pc register中保存的就是栈帧中的return address,如果此时cpu切换到另外一个线程上了,那么线程切换回来时,还可以从pc register中取出return address,继续执行下一个栈帧。
新生代使用标记-复制算法,老年代使用标记-清除/标记压缩算法。
部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型。G1是逻辑分代,物理不分代。除此之外不仅逻辑分代,而且物理分代。
新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace
动态年龄:(不重要) https://www.jianshu.com/p/989d3b06a49d
分配担保:(不重要) YGC期间 survivor区空间不够了 空间担保直接进入老年代 参考:https://cloud.tencent.com/developer/article/1082730
Linux下1.8版本默认的垃圾回收器到底是什么?
JVM的命令行参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
HotSpot参数分类
标准: - 开头,所有的HotSpot都支持
非标准:-X 开头,特定版本HotSpot支持特定命令
不稳定:-XX 开头,下个版本可能取消
java -version
java -X
试验用程序:
import java.util.List;
import java.util.LinkedList;
public class HelloGC {
public static void main(String[] args) {
System.out.println("HelloGC!");
List list = new LinkedList();
for(;;) {
byte[] b = new byte[1024*1024];
list.add(b);
}
}
}
所谓调优,首先确定,追求啥?吞吐量优先,还是响应时间优先?还是在满足一定的响应时间的情况下,要求达到多大的吞吐量…
问题:
科学计算,吞吐量。数据挖掘,thrput。吞吐量优先的一般:(PS + PO)
响应时间:网站 GUI API (1.8 G1)
调优,从业务场景开始,没有业务场景的调优都是耍流氓
无监控(压力测试,能看到结果),不调优
步骤:
测试代码:
package com.mashibing.jvm.gc; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */ public class T15_FullGC_Problem01 { private static class CardInfo { BigDecimal price = new BigDecimal(0.0); String name = "张三"; int age = 5; Date birthdate = new Date(); public void m() {} } private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50, new ThreadPoolExecutor.DiscardOldestPolicy()); public static void main(String[] args) throws Exception { executor.setMaximumPoolSize(50); for (;;){ modelFit(); Thread.sleep(100); } } private static void modelFit(){ List<CardInfo> taskList = getAllCardInfo(); taskList.forEach(info -> { // do something executor.scheduleWithFixedDelay(() -> { //do sth with info info.m(); }, 2, 3, TimeUnit.SECONDS); }); } private static List<CardInfo> getAllCardInfo(){ List<CardInfo> taskList = new ArrayList<>(); for (int i = 0; i < 100; i++) { CardInfo ci = new CardInfo(); taskList.add(ci); } return taskList; } }
java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.gc.T15_FullGC_Problem01
一般是运维团队首先受到报警信息(CPU Memory)
top命令观察到问题:内存不断增长 CPU占用率居高不下
top -Hp 观察进程中的线程,哪个线程CPU和内存占比高
jps定位具体java进程
jstack 定位线程状况,重点关注:WAITING BLOCKED
eg.
waiting on <0x0000000088ca3310> (a java.lang.Object)
假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁
怎么找?搜索jstack dump的信息,找 ,看哪个线程持有这把锁RUNNABLE
作业:1:写一个死锁程序,用jstack观察 2 :写一个程序,一个线程持有锁不释放,其他线程等待
为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称
怎么样自定义线程池里的线程名称?(自定义ThreadFactory)
jinfo pid
jstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用)
jstat -gc 4655 500 : 每个500个毫秒打印GC的情况
如果面试官问你是怎么定位OOM问题的?如果你回答用图形界面(错误)
1:已经上线的系统不用图形界面用什么?(cmdline arthas)
2:图形界面到底用在什么地方?测试!测试的时候进行监控!(压测观察)
jmap - histo 4655 | head -20,查找有多少对象产生
jmap -dump:format=b,file=xxx pid :
线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)
1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件
2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
3:在线定位(一般小点儿公司用不到)
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01
使用MAT / jhat /jvisualvm 进行dump文件分析
https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
jhat -J-mx512M xxx.dump
http://192.168.17.11:7000
拉到最后:找到对应链接
可以使用OQL查找特定问题对象
找到代码的问题
-XX:+UseG1GC
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。