jvm调优2.0

131 阅读6分钟

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