java编译: javac 目标文件路径
java反编译 javap 目标文件路径
java反编译 java -verbose 目标文件路径
jvm模型
下图为jvm的模型图:
**
下面详细的介绍主要的模块:
- 类加载器(class Loader)
将编译好的文件,以二进制流的形式,通过磁盘或者网络加载到jvm。在1.7以前,加载的类(包括类信息、常量、静态变量、即时编译器编译后的代码。存储在永久代的的方法区;在java8以后,类的信息存放放在元空间。
类的加载器有:
calssloader主要的目的是通过外部系统获取类的二进制流
classloader的目的的将编译好的class文件,加载到jvm中
classloader分四种:bootstrapclassloader:由C++编写,加载核心的java。*
extclassloader:java编写的加载扩展库javaX。*
appclassloader:java编写的,加载程序所在的目录
类的加载使用的是委派双亲方法:每次先从自定义类往上确认是否已经加载了该类,如果没加载,让更高级的类加载器去加载类,这样的目的是防止多次加载类。好处(避免重复加载 + 避免核心类篡改)
类的装载过程如下:
用户加载类的加载方法:
1、隐式加载:通过new对象,触发jvm去加载类;
2、显式加载:load class 、forname 等;
oadclass和forname的区别:
Class.forName() 方法获得 Class 对象是已经执行完初始化的了,static参数也也会被初始化;
loadClass() 方法获得的 Class 对象只完成了类加载过程中的第一步:加载,后续的操作均未进行,只有在newInstance才会去执行static块。所以spring框架中
loadClass()的使用场景:
如果是以 classpath 的方式来加载,就需要使用 ClassLoader 的 loadClass() 方法来加载。之所以这样做,是和 Spring IOC 的 Lazy Loading 有关,即延迟加载。Spring IOC 为了加快初始化的速度,大量的使用了延迟加载技术,而使用 ClassLoader 的 loadClass() 方法不需要执行类加载过程中的链接和初始化的步骤,这样做能有效的加快加载速度,把类的初始化工作留到实际使用到这个类的时候才去执行。
总结对比:通过new,可以直接传入参数;通过反射显式加载,需要通过反射,设置构造参数的值。
-
反射(反射可以获取任意类的任意属性和方法)
-
栈介绍 大概了解
java虚拟机栈(stack)java方法执行的内存模型,一个线程配一个栈。内部存储的是局部变量表,操作栈,动态链接,返回地址。只有当方法调用返回的时候,栈帧才会被释放
-
本地方法栈:主要作用于native方法;
-
计数器:当前线程执行字节码的行号指示器,改变指示器的值,去选取下一条需要执行的命令,由于多线程执行的时候,cpu的在一个时刻只能执行一个线程,所以每个线程维护可私有的的计数器,便于切换回来后,程序可以继续执行,不会发生内存泄漏,它只堆java的程序进行计数,不计数native程序
-
堆介绍
堆是线程共享的,创建的对象都会存在于堆中。
堆在1.7的模型是:新生代(eden区,survival1,survival2),老年代、永久代(方法区和多代垃圾回收后,保存下来的对象)
java7以后,常量池已经在堆中
java8后,老年代已经被元空间替换,老年代和元空间的区别是老年代使用的是jvm的堆内存,元空间使用的是计算机的本地内存,这样元数据的大小不再由MaxPermSize决定,而是实际的计算机内存空间
java8以前,常量池在永久代中,如果创建的对象过多,会出现oom,java8后,被移动到了堆中
- 垃圾回收算法及各垃圾回收器介绍
常见的垃圾回收算法:引用计数法、复制算法、标记清除、标记整理
标记清除算法的流程:
垃圾回收器介绍
-
串行垃圾收集器 单线程执行垃圾回收,其他线程停止工作
-
并行垃圾收集器 多线程垃圾回收,其他用户线程挂起,比串行的性能好
-
cms垃圾收集器 用户线程和垃圾回收线程可同时运行,适合用户互联网交互频繁的场景 下图为cms的工作流程
初始化标记,从rootset开始标记没有被应用的对象
并发标记(将初始化标记的时候还没有标记完的对象,继续标记)
预处理,(确认哪些需要删除,哪些不删除)
重新标记,将预处理和并发标记期间产生的垃圾,再标记
- G1垃圾收集器
G1垃圾收集器
- 1.7后可以使用,eden、survival、tenured的内存不连续,分成可一个个的region,每个region是1M到32M不等(region内存设置-XX:G1HeapRegionSize=n)
- 使用的算法:标记整理,局部使用复制算法,从而不产生碎片
- 只在逻辑上分代,空间上不连续
上图说明: 1、每个region可能是不同的代,其中humogous存放大的对象 2、新生代的垃圾收集依然停止所以线程执行,将内存拷贝到老年代或者suvivor区
G1收集器的常用参数
1、相对cms的优势是G1是分区收集,集合了标记和复制算法,避免产生碎片空间 2、G1的stop the world (stw)的时间是可设定的
- jvm垃圾收集器的配合使用(下面的收集器,只要启动一个,默认关联的会启用)
排查问题推荐的工具
动态扩容引起的空间震荡 描述: 服务刚刚启动时 GC 次数较多,最大空间剩余很多但是依然发生 GC 解决: 尽量将成对出现的空间大小配置参数设置成固定的,如 -Xms 和 -Xmx,-XX:MaxNewSize 和 -XX:NewSize,-XX:MetaSpaceSize 和 -XX:MaxMetaSpaceSize 等。
工作常用的指令
查看垃圾回收情况 jstat -gc 进程号 间隔时间 次数;
查看堆的配置信息 和使用情况 jmap -heap 进程号;
查看内存中堆的数量和大小 jmap -histo 进程号 | more
jmap -histo:live 进程号 | more
将某一时刻的内存情况保存到文件
jmap -dump:format=b,file=dump.dat 进程号
查看保存下来的二进制文件
jhat -port 8888 文件路径
在浏览器访问服务的8888端口(需要开启服务器的防火墙)
jhat还有查询的功能,查看对象内存多大的有哪些
jvm
堆和栈的问题排查
jstack 进程号 >path:\xx.log dump出栈信息
- 工具: 优化性能 查看gc cause 工具: gceasy
排查问题 jvm运行情况 jprofiler