JVM知识点

类加载的过程:加载->连接(验证,准备,解析)->初始化->使用->卸载

加载:查找并加载类文件的二进制数据

连接:将已经读入内存的类的二进制数据合并到JVM运行时环境中去,包含以下几个步骤:1)验证:确保被加载的类的正确性 2)准备:为类的静态变量分配内存并初始化他们 3)解析:把常量池中的符号引用转换成直接引用

初始化:为类的静态变量赋初始值

 

类加载要完成的功能

1.通过类的全限定名来获取该类的二进制字节流

2.把二进制字节流转化为方法区的运行时数据结构

3.在堆上创建一个java.lang.Class对象(对象.getClass()),用来封装类在方法区内的数据结构,并向外提供了访问方法区内数据结构的接口

 

加载类的方式

1.最常见的方式:本地文件系统中加载,从jar等归档文件中加载

2.动态的方式:将java源文件动态编译成class

3.其他方式:网络下载,从专有数据库中加载等等

 

类加载器

java虚拟机自带的加载器包括以下几种:

1.启动类加载器BootstrapClassLoader

2.扩展类加载器ExtensionClassLoader  (jdk9之后是平台类加载器PlatformClassLoader)

3.应用程序类加载器AppClassLoader (我们写的类就是它来加载的)

4.用户自定义的加载器,是java.lang.ClassLoader的子类,用户可以定制类的加载方式,自定义类加载器的加载顺序是在所有系统类加载器的后面

 

类加载器的关系

 

 

类加载器说明

1.启动类加载器:用于加载启动的基础模块类,比如:java.base ,java.management ,java.xml等等。启动类加载器不允许被改变和使用,String.getClass().getClassLoader()=null,返回的是null

2.平台类加载器:用于加载一些平台相关的模块,比如:java.scripting ,java.compiler* ,java.corba* 等等

3.应用程序类加载器:用于加载应用级别的模块,比如:jdk.compiler ,jdk.jartool ,jdk.jshell等等,还加载classpath路径中的所有类库 

4.java程序不能直接引用启动类加载器,直接设置classLoader为null,默认就使用启动类加载器

5.类加载器并不需要等到某个类“首次主动使用”的时候才加载它,jvm规范允许类加载器在预料到某个类将要被使用的时候就预先加载它

6.如果在加载的时候.class文件缺失,会在该类首次主动使用时报LinkageError错误,如果一直没被使用就不会报错

 

双亲委派模型

JVM中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器外,其余的类加载器都要有父级加载器。这里的父子关系是组合而不是继承,工作过程如下:

1.一个类加载器收到类加载请求后,首先搜索它的内建加载器定义的所有“具名模块”

2.如果找到了合适的模块定义,将会使用该加载器来加载

3.如果class没有在这些加载器定义的具名模块中找到,那么将会委托给父级加载器,直到启动类加载器

4.如果父级加载器也不能完成加载请求,比如在它的搜索路径下找不到这个类,那么子类加载器才来加载

5.在类路径下找到的类将成为这些加载器的无名模块

 

双亲委派模型说明

1.双亲委派模型对于保证java程序的稳定运行很重要

  ①避免同样的字节码重复加载

  ②出于安全性考虑,防止javaAPI中的类被随意替换

2.实现双亲委派的代码在java.lang.ClassLoader的loadClass()方法中,如果自定义类加载器的话,推荐覆盖实现findClass()方法

3.如果有一个类加载器能加载某个类,称为定义类加载器,所有能成功返回该类的Class的类加载器都被称为初始类加载器

4.如果没有指定父加载器,默认就是启动类加载器

5.每个类加载器都有自己的命名空间,命名空间由该加载器及其所有父加载器所加载的类构成,不同的命名空间,可以出现类的全限定名相同的情况

6.运行时包由同一个类加载器的类构成,决定两个类是否属于同一个运行时包,不仅要看全限定名是否一样,还要看定义类加载器是否相同。只有属于同一个运行时包的类才能实现相互包内可见

 

破坏双亲委派模型

1.双亲委派模型有个问题:父加载器无法向下识别子加载器加载的资源

2.为了解决这个问题,引入了线程上下文类加载器,可以通过Thread的setContentClassLoader()进行设置

java.sql.DriverManager源码

3.另外一种典型的情况就是实现热替换,比如OSGI的模块化热部署,它的类加载器就不是严格按照双亲委派模型,可能在平级的类加载器中执行了

 

类连接主要验证的内容

1.类文件结构检查:按照JVM规范规定的类文件结构进行

2.元数据验证:对字节码描述的信息进行语义分析,保证其符合java语言规范要求

3.字节码验证:通过对数据流和控制流进行分析,确保程序语义是合法和符合逻辑的。这里主要对方法体进行校验

4.符号引用验证:对类自身以外的信息,也就是常量池中的各种符号引用,进行匹配校验

 

类连接中的解析

所谓解析就是把常量池中的符号引用转换成直接引用发的过程

符号引用:以一组无歧义的符号来描述所引用的目标,与虚拟机的实现无关

直接引用:直接指向目标的指针、相对偏移量、或是能间接定位到目标的句柄,和虚拟机的实现相关

主要针对:类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符

 

类的初始化

类的初始化就是为类的静态变量赋初始值,或者说是执行类构造器<init>方法的过程 (不是实例构造器方法)

1)如果类还没有加载和连接,就先加载和连接

2)如果类存在父类,且父类没有初始化,就先初始化父类

3)如果类中存在初始化语句,就依次执行这些初始化语句

4)如果是接口:

                   a.初始化一个类的时候,并不会先初始化它实现的接口

                   b.初始化一个接口时,并不会初始化它的父接口

                   c.只有当程序首次使用接口里面的变量或者调用接口的方法时,才会初始化接口

5)调用ClassLoader类的loadClass方法来加载类的时候,并不会初始化这个类,加载类不是对类的主动使用

 

类的初始化时机

java程序对类的使用分为主动使用和被动使用。JVM在每个类或接口“首次主动使用”时才初始化他们;被动使用类不会导致类的初始化。

主动使用的情况:

                       a.创建类实例

                       b.访问某个类或接口的静态变量

                       c.调用类的静态方法

                       d.反射某个类

                       e.初始化某个类的子类,而父类还没有初始化

                       f.JVM启动的时候运行的主类

                       g.定义了default方法的接口,当接口的实现类初始化时

被动使用的情况:

                       1.通过子类去调用父类的静态变量 。例如MyChidl.parentStr,不会初始化子类,会初始化父类

                       2.数组的方式。例如MyChild[] mcs = new MyChild[2],不会初始化类

                       3.访问常量也不会初始化类。例如public static final String childStr = "now in child!"

 

类的初始化机制和顺序



类的卸载

当代表一个类的Class对象不在被引用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载

JVM自带的类加载器加载的类。是不会卸载的。由用户自定义的类加载器加载的类是可以卸载的

 

JVM内存分配基础

JVM的简化架构

运行时数据区:包括方法区、虚拟机栈、本地方法栈、堆、程序计数器(PC寄存器)

PC寄存器:

1.每个线程都拥有一个PC寄存器,是线程私有的,用来存储指向下一条指令的地址,也可以看着是当前程序所执行的字节码的行号指示器,也可以看着是程序流的指示器

2.在创建线程的时候创建相应的PC寄存器

3.执行本地方法(执行JNI方法)时,PC寄存器的值为undefined

4.是一块比较小的内存空间,是唯一一个在JVM规范中没有规定OutOfMemoryError的内存区域,不会内存溢出

java栈

1.栈由一系列帧组成,是线程私有的

2.帧用来保存一个方法的局部变量表、操作数栈(java没有寄存器,所有参数传递使用操作数栈)、常量池指针、动态链接、方法返回值等

3.每一次方法调用创建一个帧,并压栈,退出方法的时候,修改栈顶指针就可以把栈桢中的内容销毁

4.局部变量表存放了编译期可知的各种基本数据类型和引用类型,每个slot(插槽)存放32位的数据,long、double占两个槽位

5.栈的优点:存取速度比堆块,仅次于寄存器

6.栈的缺点:存在栈中的数据大小、生命周期是在编译期决定的,缺乏灵活性

java堆

用来存放应用系统创建的对象和数组,所有线程共享java堆

GC主要管理堆空间

堆的优点:运行期动态分配内存大小,自动进行垃圾回收

堆的缺点:效率相对较慢

方法区

方法区是线程共享的,用来保存类的结构信息(运行时的常量池、静态变量、常量、类元信息)

通常和元空间关联在一起,具体跟JVM的实现和版本有关

JVM规范把方法区描述为堆的一个逻辑部分,但它有一个别名Non-heap(非堆),是为了与java堆区分开

运行时常量池

是Class文件中每个类或接口的常量池表,在运行期间的表示形式,通过包括:类的版本,字段、方法、接口等信息

在方法区中分配

通常在加载类或接口到JVM中后,就创建相应的运行时常量池

本地方法栈

在JVM中用来支持native方法执行的栈就是本地方法栈。是线程私有的

 

Java堆内存概述

java堆用来存放应用系统创建的对象和数组,所有线程共享java堆

java堆是在运行期动态分配内存大小,自动进行垃圾回收

java垃圾回收主要就是回收堆内存,对分代GC来说,堆也是分代的

 

java堆的结构

1. 新生代用来存放新分配的对象;新生代中经过垃圾回收,没有被回收掉的对象,被复制到老年代

2.老年代存储的对象比新生代存储的对象的年龄大的多

3.老年代存储一些大对象,大对象会直接分配到老年代

4.整个堆的大小=新生代+老年代

5.新生代=Eden去+存活区(Survivor)

6.从JDK8开始去掉了持久代(用来存放Class、Method等元信息的区域),取而代之的是元空间(MetaSpace),元空间并不在虚拟机里面,而是直接使用本地内存

 

对象的内存布局

对象在内存中存储的布局(以HotSpot虚拟机为例)分为:对象头,实例数据和对齐填充

1.对象头,包括两部分

  (1)Mark Word:存储对象自身的运行数据,如:HashCode、GC分代年龄、锁状态标志等

  (2)类型指针:指向它的类元信息(在方法区)的指针

2.实例数据:真正存放对象实例数据的地方

3.对齐填充:这部分不一定存在,也没有特别的含义,仅仅是占位符。因为HotSpot要求对象起始地址都是8字节的整数倍,如果不是,就对齐

 

对象的访问定位

.对象的访问定位:在JVM规范中只规范了reference类型是一个指向对象的引用,但没有规定这个引用具体如何去定位、访问堆中对象的具体位置

因此对象的访问方式取决于JVM的实现,目前主流的有:使用句柄或使用指针两张方式

1.使用句柄:Java堆中会划分出一块内存来作为句柄池,reference中存储句柄的地址,句柄中存储对象实例数据和类元数据的地址

2.使用指针:java堆中存放访问类元数据的地址,reference存储的是对象实例数据的地址

 

 

Java内存分配参数

Trace跟踪参数 (版本jdk9+)

打印GC的简要信息:-Xlog:gc

打印GC详细信息:-Xlog:gc*

指定GC log的位置,以文件输出:-Xlog:gc:garbage-collection.log

每一次GC后,都打印堆信息:-Xlog:gc+heap=debug

Java堆的参数

-Xms:堆的大小,默认为物理内存的1/64

-Xmx:堆的最大值,默认为物理内存的1/4

建议-Xms和-Xmx设置相等,避免每次GC后调整堆的大小,减少系统内存分配的开销

-Xmn:新生代的大小,默认整个堆的3/8  (JDK8是1/3)

设置垃圾回收器 -XX:+UseConcMarkSweepGC 使用CMS垃圾收集器  -XX:+UseG1GC 使用G1收集器

-XX:+UseConcMarkSweepGC -Xms10m -Xmx10m -Xmn3m -Xlog:gc

保存GC日志

-XX:+UseConcMarkSweepGC -Xms10m -Xmx10m -Xmn3m -Xlog:gc+heap=debug:logs/mygc.log -XX:+HeapDumpOnOutOfMemoryError

-XX:NewRatio:老年代与新生代的比值。如果设置Xms=Xmx,且设置了Xmn的情况下,该参数不用设置

-XX:SurvivorRatio:Eden区和Survivor区的大小比值

-XX:+HeapDumpOnOutOfMemoryError:OOM时导出堆文件

-XX:+HeapDumpPath:导出OOM的路径

-XX:OnOutOfMemoryError:在OOM时,执行一个脚本

Java栈的参数

-Xss:通常只有几百k,决定了函数调用的深度

元空间的参数

-XX:MetaspaceSize:元空间初始大小

-XX:MaxMetaspaceSize:最大空间,默认没有限制

-XX:MinMetaspaceFreeRatio:在GC之后,最小的Metaspace剩余空间容量的百分比

-XX:MaxMetaspaceFreeRatio:在GC之后,最大的Metaspace剩余空间容量的百分比

 

字节码执行引擎

概述

JVM的字节码执行引擎,功能就是输入字节码文件,然后对字节码进行解析并处理,最后输出执行结果

实现方式有通过解释器直接解释字节码文件,或者是通过即时编译器产生本地代码,也就是编译执行,当然也可能两者皆有

栈桢概述

栈桢是用于支持JVM方法调用和方法执行的数据结构

栈桢随着方法的调用而创建,随着方法结束而销毁

栈桢里面存储了方法的局部表里表、操作数栈、动态链接、方法返回地址等信息

局部变量表:用来存放方法参数和方法内部定义的局部变量的存储空间

  1.以变量槽slot为单位,一个slot槽存放32位以内的数据类型

  2.对于64位的数据占2个slot

  3.对于实例方法,第0位slot存放的是this,然后从1到n,依次分配给参数列表。静态方法是从0开始

  4.然后根据方法体内部定义的变量顺序和作用域来分配slot

  5.slot是复用的,以节省栈桢的空间,这种设计可能会影响到系统的垃圾收集行为

 

操作数栈

操作数栈:用来存放方法运行期间,各个指令操作的数据

  1.操作数栈中元素的数据类型必须和字节码指令的顺序严格匹配

  2.虚拟机在实现栈桢的时候可能会做一些优化,让两个栈桢出现部分重叠区域,以存放公用数据

 

动态链接

动态链接:每个栈桢持有一个指向运行时常量池中该栈桢所属方法的引用,以支持方法调用过程的动态链接

  1.静态解析:类加载的时候,符号引用转换成直接引用

  2.动态链接:运行期间转化为直接引用

 

方法返回地址

方法返回地址:方法执行后返回的地址

 

方法调用

方法调用:就是确定具体调用哪一个方法,不涉及方法内部的执行过程

  1.部分方法是直接在类加载的解析阶段,就确定了直接引用关系

  2.但是对于实例方法,也称虚方法,因为重载和多态,需要运行期动态委派

 

分派

分派:分为静态太分派和动态分派

  1.静态分派:所有依赖静态类型来定位方法执行版本的分派方式,比如:重载方法

  2.动态分派:根据运行期的实际类型来定位方法执行版本的分派方式,比如:覆盖方法、接口的实现

单分派和多分派:就是按照分派思考的维度,多余一个的就算多分派,只有一个的称为单分派

如何执行方法中的字节码指令:JVM通过基于栈的字节码解释执行引擎来执行指令,JVM的指令集也是基于栈的

 

垃圾回收概述

什么是垃圾:简单说就是内存中已经不再使用的内存空间就是垃圾

引用计数算法:给对象添加一个引用计数器,有访问就加1,引用失效就减1

优点:失效简单,效率高;缺点:不能解决对象之间循环引用的问题

可达性分析算法(根搜索算法):从根节点向下搜索对象节点,搜索走过的路径称为引用链,当一个对象到根之间没有连通的话,则该对象不可用

可作为GC Roots的对象包括:虚拟机栈(栈桢局部变量)中引用的对象、方法区类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象

HotSpot使用了一组叫做OopMap的数据结构达到准确式GC的目的

在OopMap的协助下,JVM可以很快的做完GC Roots枚举。但是JVM并没有为每条指令生成一个OopMap

记录OopMap的这些“特定位置”被称为安全点,即当前线程执行到安全点后才允许暂停进行GC

如果一段代码中,对象引用关系不会发生变化,这个区域中任何地方开始GC都是安全的,这个区域称为安全区域

 

引用分类

强引用:new出来的对象,Object a = new A(),不会被回收

软引用:还有用但不必须的对象,内存不够时才会回收掉。用SoftReference来表示

弱引用:非必须对象,发生GC一定会被回收掉。用WeakReference来表示

虚引用:也叫幽灵引用或幻影引用,是最弱的引用,GC时会回收掉。用PhantomReference来表示

 

跨代引用

跨代引用:一个代中的对象引用另一个代中的对象

跨代引用假说:跨代引用相对于同代引用来说只是极少数

隐含推论:存在相互引用关系的两个对象,是应该倾向于同时生存或同时消亡的

 

记忆集

记忆集:一种用于记录从非收集区域向收集区域的指针集合的抽象数据结构

写屏障

写屏障可以看着是JVM对“引用类型字段赋值”这个动作的AOP

 

判断是否垃圾的步骤

1.根搜索算法判断不可用

2.看是否有必要执行finalize()方法(在该方法中对象能自救,但是不建议覆盖finailze方法)

3.两个步骤执行完对象仍然没有被使用,就属于垃圾

 

GC类型

MinorGC/YoungGC:发生在新生代的收集动作

MajorGC/OldGC:发生在老年代的GC,目前只有CMS收集器会有单独收集老年代的行为

MixedGC:收集整个新生代及部分老年代,目前只有G1收集器会有这种行为

FullGC:收集整个Java堆和方法区的GC

 

Stop-The-World

STW是Java中一种全局暂停的现象,多半由于GC引起。所谓全局停顿,就是所有java代码停止运行,native代码可以执行,但是不能和JVM交互

其危害是长时间服务停止,没有响应;对于HA系统,可能引起主备切换,严重危害生产环境

 

垃圾收集类型

串行收集:GC单线程内存回收,会暂停所有的用户线程,比如:Serial

并行收集:多个GC线程并行工作,此时用户线程是暂停的,比如:Parallel

并发收集:用户线程和GC线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程,比如:CMS

 

判断类无用的条件

JVM中该类的所有实例都已经被回收

加载该类的ClassLoader已经被回收

没有任何地方引用该类的Class对象

无法在任何地方通过反射访问这个类

 

垃圾回收算法

标记清除算法

优点是简单

缺点是:效率不高,标记和清除的效率都不高;标记清除后会产生大量不连续的内存碎片,从而导致分配大对象时触发GC

 

复制算法

复制算法:把内存分成两块完全相同的区域,每次使用其中一块,当一块使用完了,就把这块上还存活的对象拷贝到另一块,然后把这块清除掉

优点:实现简单、运行高效,不用考虑内存碎片

缺点:内存浪费

JVM实际实现中,是将新生到内存分为一块较大的Eden区和两块较小的Survivor区,每次使用Eden和一块Survivor,回收时,把存货的对象复制到另一块Survivor

HotSpot默认的Eden和Survivor比是8:1,也就是每次能用90%的新生代空间

如果Survivor空间不够,就要依赖老年代进行空间分配担保,把放不下的对象直接进入老年代

 

空间分配担保

空间分配担保:当新生代进行垃圾回收后,新生代的存活区放置不下,那么需要把这些对象放置到老年代去的策略,也就是老年代为新生代的GC做空间分配担保,步骤如下:

  1.在发生MinorGC前,JVM会检测老年代的最大可用的连续空间,是否大于新生代所有对象的总空间,如果大于,可以确保MinorGC是安全的

  2.如果小于,那么JVM会检查是否设置了允许担保失败,如果允许,则继续检查老年代最大可用的连续空间,是否大于历次晋升到老年代对象的平均大小

  3.如果大于,则尝试进行MinorGC

  4.如果不大于,则进行FullGC

 

标记整理算法

标记整理算法:由于复制算法在存活对象比较多的时候,效率较低,且浪费空间,因此老年代一般不会选用复制算法,老年代多用标记整理算法

标记过程跟标记清除算法一样,但后续不是直接清除垃圾对象,而是让所有存活的对象向一端移动,然后直接清除边界以外的内存空间

 

垃圾收集器

串行收集器

Serial收集器/Serial Old收集器,是一个单线程的收集器,在垃圾收集时,会Stop-The-World

优点是简单,对于单CPU,由于没有多线程交互的开销,可能更高效,是默认的Client模式下的新生代收集器

使用-XX:UseSerialGC来开启,会使用:Serial+Seria Old的收集器组合

新生代使用复制算法,老年代使用标记整理算法

 

并行收集器

ParNew收集器:使用多线程进行垃圾回收,在垃圾收集时,会Stop-The-World

在并发能力好的CPU环境里,它停顿的时间要比串行收集器短;但对于单CPU或并发能力较弱的CPU,由于多线程的交互开销,效率可能比串行收集器更差

是Server模式下首选的新生代收集器,且能和CMS收集器配合使用

-XX:ParallelGCThreads:指定线程数,最好和CPU核数一致

 

新生代Parallel Scavenge收集器

新生代Parallel Scavenge收集器是一个用于新生代、使用复制算法,并行收集器,配合Parallel Old使用

跟ParNew相似,但更关注吞吐量,能最高效的利用CPU,适合运行后台项目

使用-XX:+UseParallelGC来开启

 

CMS收集器

CMS(Concurrent Mark and Sweep 并发标记清除)收集器分为4个步骤:

  1.初始标记:只标记GC Roots能直接关联到的对象,暂停用户线程

  2.并发标记:进行GC Roots Tracing的过程

  3.重新标记:修正并发标记期间,因程序运行导致标记发生变化的对象,暂停用户线程

  4.并发清除:并发回收垃圾对象

在初始标记和重新标记阶段会暂停用户线程,会Stop-The-World

使用在老年代,使用标记清除算法,多线程并发收集

优点:低停顿、并发执行

缺点:并发执行,对CPU资源压力大;无法处理CMS收集器GC过程中产生的垃圾,可能导致FullGC;采用的标记清除算法会产生大量不连续的内存碎片,从而在分配大对象时可能触发FullGC

开启:-XX:+UseConcMarkSweepGC,使用ParNew + CMS + Serial Old的收集器组合,Serial Old为CMS出错的后备收集器

 

G1收集器

G1(Garbage-First)收集器:是一款面向服务端应用的收集器,与其他收集器相比有如下特点:

  1.G1把内存划分成多个独立的区域(Region)

  2.G1仍采用了分代思想,保留了新生代和老年代,但它们不在是物理隔离的, 而是一部分Region的集合,且不需要Region是连续的

  3.G1能充分利用多CPU、多核环境听见优势,尽量缩短STW

  4.G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片

  5.G1的停顿可预测,能明确指定在一个时间段内,消耗在垃圾收集上的时间不能超过多长时间

  6.G1跟踪各个Region里面垃圾堆的价值大小,在后台维护一个优先列表,每次根据允许的时间来回收价值最大的区域,从而保证在有限时间内的高效收集                      

跟CMS类似,有分为四个阶段:

  1.初始标记:只标记GC Roots能直接关联到的对象

  2.并发标记:进行GC Roots Tracing的过程

  3.最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象

  4.筛选回收:根据时间来进行最大化价值的回收

开启:-XX:+UseG1GC

-XX:MaxGCPauseMillis=n:最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间

-XX:InitiatingHeapOccupancyPercent=n:堆占用了多少的时间就触发GC,默认为45

-XX:NewRatio=n:默认为2

-XX:SurvivorRatio=n:默认为8

-XX:MaxTenuringThreshold=n:新生代到老年代的岁数,默认是15

-XX:G1HeapRegionSize=n:设置G1区域的大小。值是2的幂,范围是1MB到32MB。目标是根据最小的Java堆划分出约2048个区域

 

 

ZGC收集器

ZGC收集器:JDK11加入的具有实验性质的低延迟收集器

ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%

ZGC的新技术:着色指针 和 读屏障

 

GC性能指标

吞吐量=应用代码执行的时/运行的总时间

GC负荷=GC时间/运行的总时间

暂停时间=发生STW的总时间

GC频率:一个时间段发生GC的次数

反应速度:对象成为垃圾到被回收的时间

交互式应用通常希望暂停时间越少越好

 

JVM内存配置原则

新生代尽可能设置大点,如果太小会导致:

  1.YGC次数更加频繁

  2.可能导致YGC后的对象进入老年代,如果此时老年代满了,会触发Full GC

  3.对老年代,针对响应时间优先的应用:由于老年代通常采用并发收集器,因此其大小要综合考虑并发量和并发持续时间等参数

 

锁优化之偏向锁

偏向锁是在无竞争情况下,直接把整个同步消除了,连乐观锁都不用,从而提高性能;所谓的偏向就是偏心,即锁会偏向于当前已经占用锁的线程

只要没有竞争,获得偏向锁的线程,在将来进入同步块,也不需要做同步

当有其他的线程请求相同的锁时,偏向模式结束

如果程序中大多数锁总是被多个线程访问的时候,也就是竞争比较激烈,偏向锁会降低性能

使用-XX:+UseBiasedLocking来禁用偏向锁,默认开启

 

JVM中获取锁的步骤

1.会先尝试偏向锁,然后尝试轻量级锁

2.再然后尝试自旋锁

3.最好尝试普通锁,使用OS互斥量在操作系统层挂起

 

同步代码的基本规则

1.尽量减少锁持有的时间

2.进来减少锁的粒度

 

JVM性能监控

命令行工具

jps

jps(JVM Process Status Tool):主要用来输出JVM中运行的状态信息,语法格式:jps [options] [hostid]

jinfo

打印给定线程或核心文件或远程调试服务器的配置信息。语法格式:info [option] pid  #pid进程号 

jstack

jstack主要用来查看某个java进程内的线程堆栈信息。语法格式:jstacj [option] pid

jmap

jmap用来查看堆内存使用状况,语法格式:jmap [option] pid

jstat

JVM统计检测工具,查看各个区内存和GC情况

jstated

虚拟机的jstat的守护进程,主要用来监控JVM的创建与终止,并提供一个接口,以允许远程监视工具附加到在本地系统上允许的JVM

jcmd

JVM诊断命令工具,将诊断命令请求发送到正在允许的java虚拟机,比如可以用来导出堆,查看java进程,导出线程信息,执行GC等

 

JVM图形监控工具

  1.jconsole

  jdk自带的一个用于监视java虚拟机的符号JMX的图形工具。它可以监视本地和远程的JVM,还可以监视和管理应用程序

  2.jmc

  jmc(JDK Mission Control)Java任务监控(JMC)客户端包括用于监视和管理java应用程序的工具,而不会引入通常与这些类型的工具相关联的性能开销

  下载地址:https://www.oracle.com/java/technologies/javase-downloads.html

  3.VisualVM

  一个图形工具,它提供有关在java虚拟机中运行的基于java技术的应用程序的详细信息

  java VisualVM提供内存和CPU分析,堆转储分析,内存泄漏检测,访问MBean和垃圾回收

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值