jvm优化

jvm优化

一:jvm的位置

jvm优化_第1张图片

jvm是运行在操作系统之上的,它与硬件没有直接的交互

二:jvm体系结构概览

jvm优化_第2张图片
– 灰色的地方绝对不会垃圾回收,jvm调优就是调堆内存。

三:类加载器

负责加载class文件,class文件在文件开头有特定的文件标识,并且classloder只负责class文件的加载,至于它是否可以运行,则由Execution Engine 决定
jvm优化_第3张图片

car.class 就是我们编写的类模板,封装了自己的属性和行为,但是.class文件是存储在屋里内存中的,我们通过calssloader类的加载到jvm中,此时可以得到car class这里的car class就相当于是jvm中的模板,那么创建car的实例就轻而易举了,我们学过了反射,可以知道通过反射可以获取类的属性和行为,就是因为我们获取了jvm中的car class

1.启动类加载器

这个类加载器负责加载放在\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库。用户无法直接使用。

2.扩展类加载器:负责\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,用户可以直接使用。

3.应用程序类加载器:是classloader中getsystemClassLoader()方法的返回值。它负责用户路径(classPath)所指定的类库。用户可以直接使用,如果用户没有自己定义类加载器,默认使用这个。

4.自定义加载器:用户自己定义的加载器。(一般只有做框架才回用到)

jvm双亲委派机制和沙箱机制

双亲委托:

一个类加载器查找class和resource时,时通过委托模式进行的,它首先判断这个class是不是已经加载成功,如果没有的话它不是自己进行查找,而是先通过父类加载器,然后递归下去,直到Bootstrap CalssLoader 如果到最后它找到了直接返回,如果没有找到则是一级一级返回,最后到达自身去查找这些对象。这种机制叫做双亲委托

沙箱机制

沙箱机制就是双亲委派模型的安全机制

自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.string类,最终自定义的类加载器无法加载java.lang.String类,这样的好处是可以保证不被恶意的修改系统原有的类(不是绝对安全的,还可以自定义classloader)

本地方法栈和本地方法接口(Native):

可以发下Object类中有很多方法是使用native修饰,并且没有方法体,那说明这些方法超出了java的范围,所以底层实现需要使用iNI–》java Native interface

native修饰的方法就是告诉java本身,此时需要调用外部的本地类库即c/c++类库来执行这个方法

Native interface 本地接口:

融合不同的编程语言为javay用,它的初衷是为融合c或c+程序,java发型初期,c/c+流行,要想立足,必须调用它,就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是NatIveMethod Stack中登记native方法,在Execution Engine 执行时加载native libraies。目前该方法使用少,大多在于硬件相关的应用。

现在我们知道java虚拟机中有java栈和本地方法栈,那么我们创建的对象在哪里?

new Person()

不带native的进入到java栈中,只有带native的进入本地方法栈,所以对象在java栈中,我们平常说的栈就是java栈。

Pc寄存器

每个线程都会有一个程序计数器,是线程私有的,就是一个指针,执行了方法区中的方法字节码(存储的是下一条指令的地址,即将执行的代码),有执行引擎读取吓一跳指令,是一个非常小的内存空间,几乎可以忽略。

作用:在代码中同时存在三个调用方法,从上到下,依次执行,pc寄存器起到了作用

Method Area方法区

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说:所有定义的方法的信息都保存在该区域,此区属于共享区间。

存放在方法区的:静态变量+常量+类信息(构造方法/接口定义)+运行时常量池存放在方法区中

注意:只要是被线程私有独享的疑虑没有回收,只有是线程共享才能有回收

stack 栈是什么

两个数据结构:

栈:先进后出 队列:先进先出(在栈中main方法一定在栈低)

栈也叫栈内存,主管java程序的运行,是在线程创建时创建,它的声明期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就over,生命周期和线程一致,是线程私有的。

栈中存储:8中基本类型的变量+对象引用变量+实例方法都在函数的栈内存中分配

栈帧中主要保存3类数据:本地变量:输入参数和输出参数及方法内的变量

栈操作:记录出栈入栈的操作

栈帧数据:包括类文件,方法等

StackOverError:递归过深,递归没有出口报错

OutofMemoryError:jvm空间溢出,创建对象速度高于Gc回收速度

Heap堆

一个jvm实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类,方法,常变量放到堆内存中,保存所有引用类型的真是信息,一方便执行器执行,堆内存分为三部分:

Young Generation Space 新生区 Young/new

Tenure GEneration space 养老区 Old/tenure

Permanent Space 永久区 Perm

Heap堆(java7之前)

堆内存逻辑上分为三部分:新生+养老+永久

jvm优化_第4张图片

新生区:

新生区是类的诞生到消亡的区域,一个类在这里产生,应用,最后被垃圾回收器手机,结束生命。新生区右分为两部分:伊甸区和幸存者区,所有的类都是在伊甸区被new出来。幸存区有两个:0区和1区。当伊甸园的空间用完时,程序又需要创建对象,jvm的垃圾回收器对伊甸园区进行垃圾回收即轻量垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园区的剩余对象移动到幸存0区,若幸存0区也满了在对该去进行垃圾回收,然后移动到1区。那如果1区也满后,则将其移动到养老区。若养老区也满了,在对该区进行垃圾回收,即重量垃圾回收,进行养老区的内存清理。若养老区执行了full gc 后发现依然无法进行对象的保存就会产生00m异常:Outofmemoryerror;

outofmemoryerror

如果出现了这个异常,说明java虚拟机的堆内存不够,原因有二:

1.java虚拟机的堆内存设置不够,可以通过参数-xms,-xmx来调整

2.代码中创建了大量大对象,并且长时间不能被垃圾回收器收集(存在被引用)

永久区:

永久存储区是一个常驻内存区域,用于存放jdk自身所携带的class,interface的源数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据不会被垃圾回收器回收掉的,关闭jvm才回释放此区域所占用的内存。

如果出现outofmemoryerror:permgen space 说明java虚拟机对永久代perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的三方jar包,例如:在一个tomact下部署了太多的应用,或者大量的动态反射生成的类不断被加载,最终导致Perm区被沾满。

jdk1.8及之后:无永久代,常量池1.8在元空间。

实际而言,方法区和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器后的代码等,虽然jvm规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap目的就是和堆分开。

常量池是方法区的一部分,class文件除了有类的版本,字段,方法,接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量池中存放。

堆内存调优

jvm垃圾收集:本次以jdk1.8+hotspot为例:

java1.8之后永久代由元空间所取代

java调优内存量计算

-xms设置初始分配大小,默认为物理内存的1/64

-xmx最大分配内存,默认为物理内存的1/4

-XX:+PrintGcDetails

vm参数:-Xms1024m-Xms1024m-XX:+printGcDetails

最大内存,初始化内存,打印内存日志信息

用新生代大小/1024+养老代大小/1024=初始化大小

Garbage Collection垃圾回收机制

GC:分带收集算法,频繁收集Young区,较少收集Old区,基本不动Perm区

jvm在进行gc时,并非每次对三个区一起进行回收,大部分时候回收的都是指新生代,因此Gc按照回收的区域右分了两种类型,一种是普通Gc,一种是全局GC,普通GC:只针对新生代GC全局GC针对年老代的GC偶尔伴随对新生代的GC以及对永久代的GC;

GC4大算法:

1.引用计数法

2复制算法:

3.标记清除

4.标记压缩

扩展堆外内存:

1.堆内内存:堆外内存和堆内内存是相对的两个概念,其中堆内内存是平常中接触比较多的我们在jvm参数中只要使用Xms,Xmx等参数就可以设置堆的大小和最大值,理解jvm的堆还需要直到下面这个公式:

对内内存=新生代+老年代+持久带

使用堆内内存的时候,完全遵守jvm虚拟机的内存管理机制,采用垃圾回收器统一进行内存管理,Gc会在某些特定的时间点进行一次彻底回收,也就是Full Gc,GC会对所有分配的堆内内存进行扫描,在这个过程中会对java应用程序的性能造成一定影响,还可能会产生Stop the world

2.堆外内存介绍:

和堆内内存相对应,堆外内存就是把内存对象分配在java虚拟机的堆以外的内存,这些内存直接受操作系统管理,而不是虚拟机,这样做的结果就是能够一定程度上减少垃圾回收对应用程序造成的影响。

为什么实用堆外内存

1.减少了垃圾回收

堆外内存直接受操作系统管理,这样做可以保持一个较小的堆内内存,较少垃圾收集对应用的影响。

2.提升复制速度

堆内内存有JvM管理,属于用户态,而堆外内存由OS管理,属于内核态。如果从堆内向磁盘写数据时,数据会被先复制到堆外内存,即内核缓冲区,再有OS写入磁盘,使用堆外内存避免了这个操作。

ps:堆外内存是可以通过java中的未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存

以保持一个较小的堆内内存,较少垃圾收集对应用的影响。

2.提升复制速度

堆内内存有JvM管理,属于用户态,而堆外内存由OS管理,属于内核态。如果从堆内向磁盘写数据时,数据会被先复制到堆外内存,即内核缓冲区,再有OS写入磁盘,使用堆外内存避免了这个操作。

ps:堆外内存是可以通过java中的未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存

你可能感兴趣的:(java)