JVM原理及调优使用总结

一、JVM的内部体系结构:

基本概念:JVM(JAVA虚拟机-java visual machine)由三大部分组成

(1)类装载器(ClassLoader)子系统
     作用: 用来装载.class文件
(2)执行引擎
     作用:执行字节码,或者执行本地方法
(3)运行时数据区
     方法区,堆,java栈,PC寄存器,本地方法栈

---》堆(Heap):是jvm中内存最大的区域,主要存储的是对象

JVM原理及调优使用总结_第1张图片

---》栈(Stack):栈中存的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说是可以动态变化的,但是在栈中,一个对象只对应了一个4btye的引用(堆栈分离的好处:))。为什么不把基本类型放堆中呢?因为其占用的空间一般是1~8个字节——需要空间比较少,而且因为是基本类型,所以不会出现动态增长的情况——长度固定,因此栈中存储就够了,如果把他存在堆中是没有什么意义的(还会浪费空间)。可以这么说,基本类型和对象的引用都是存放在栈中,而且都是几个字节的一个数,因此在程序运行时,他们的处理方式是统一的。但是基本类型、对象引用和对象本身就有所区别了,因为一个是栈中的数据一个是堆中的数据。最常见的一个问题就是,Java中参数传递时的问题 栈的最小单位是栈帧,一个类中含有多个方法,每个方法都对应一个栈帧,多线程中一个线程对应一个栈帧,这样不同线程之间数据就隔离开了

---》本地方法区(Native Method):执行Native方法或者API时候调用的区域,一般使用的很少

---》方法区(Method Area):是jvm规范里面的运行时数据区的一个组成部分,jvm规范中的运行时数据区还包含了:pc寄存器、虚拟机栈、堆、方法区、运行时常量池、本地方法栈。

方法区存储东西?

主要用来存储class、运行时常量池、字段、方法、代码、JIT代码等。

注意:

(1)运行时数据区跟内存不是一个概念。

(2)方法区是运行时数据区的一部分

(3)方法区是jvm规范中的一部分,并不是实际的实现,切忌将规范跟实现混为一谈。

我们再来看下永久带(Perm区 Permanet Generation):

永久带又叫Perm区,只存在于hotspot jvm中,并且只存在于jdk7和之前的版本中,jdk8中已经彻底移除了永久带,jdk8中引入了一个新的内存区域叫metaspace。

(1)并不是所有的jvm中都有永久带,ibm的j9,oracle的JRocket都没有永久带。

(2)永久带是实现层面的东西。

(3)永久带里面存的东西基本上就是方法区规定的那些东西。

因此,我们可以说,永久带是方法区的一种实现,当然,在hotspot jdk8中metaspace可以看成是方法区的一种实现。

下面我们来看下hotspot jdk8中移除了永久带以后的内存结构:

https://img2.mukewang.com/5b59198f000159c905450385.jpg

结论:

(1)方法区是规范层面的东西,规定了这一个区域要存放哪些东西

(2)永久带或者是metaspace是对方法区的不同实现,是实现层面的东西。

---》程序计数器:程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。- - 摘自《深入理解Java虚拟机》

程序计数器的特点:(1)如果线程正在执行的是Java 方法,则这个计数器记录的是正在执行的虚拟机字节码指令地址;(2)如果正在执行的是Native 方法,则这个技术器值为空(Undefined);(3)此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。因为程序计数器只是改变入栈指令的值,而不会随着程序的运行计数器所需要的空间而变化。

JVM数据类型
  Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的
值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象
本身存放在这个引用值所表示的地址的位置。

基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress
引用类型包括:类类型,接口类型和数组。

栈和堆的区别:栈是运行时的单位,而堆是存储的单位。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

二、什么是垃圾?什么是垃圾回收器?垃圾回收的算法有哪些?

垃圾? java中使用的是面向对象的类,那么是不是只要是没有引用指向的对象就一定是垃圾呢。答案是否定的,因为我们使用的对象过程中是有特殊情况的,比如两个对象相互引用,但是没有第三个对象指向他们,那么这种情况下两个对象有引用指向但是也属于垃圾,需要被回收,其他还有环形引用,即有多个对象间相互引用,但是最后形成一个环形引用,没有外界其他对象的引用,所以这种也是垃圾,也是需要被回收的,所以通过引用计数的方式来判断是否是垃圾的算法不可取,JVM是通过正向可达算法来进行垃圾的判断的,通过main方法中的任何对象作为root对象,如果程序中的任何对象对于root可达,那么就不是垃圾,否则是垃圾。

垃圾回收? GC,即就是Java垃圾回收机制。目前主流的JVM(HotSpot)采用的是分代收集算法。作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题。与C++不同的是,Java采用的是类似于树形结构的可达性分析法来判断对象是否还存在引用。即:从gcroot开始,把所有可以搜索得到的对象标记为存活对象。缺点就是:1. 有可能不知不觉浪费了很多内存。2. JVM花费过多时间来进行内存回收。3. 内存泄露

垃圾回收原则:无监控不调优

Java的垃圾回收机制:要从:“什么时候”,“对什么东西”,“做了什么”三个方面来具体分析。
       第一:“什么时候”即就是GC触发的条件。GC触发的条件有两种。(1)程序调用System.gc时可以触发;(2)系统自身来决定GC触发的时机。系统判断GC触发的依据:根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程。
       第二:“对什么东西”笼统的认为是Java对象。但是准确来讲,GC操作的对象分为:通过可达性分析法无法搜索到的对象和可以搜索到的对象。对于搜索不到的方法进行标记。    
       第三:“做了什么”最浅显的理解为释放对象。但是从GC的底层机制可以看出,对于可以搜索到的对象进行复制操作,对于搜索不到的对象,调用finalize()方法进行释放。

       具体过程:当GC线程启动时,会通过可达性分析法把Eden区和From Space区的存活对象复制到To Space区,然后把Eden Space和From Space区的对象释放掉。当GC轮训扫描To Space区一定次数后,把依然存活的对象复制到老年代,然后释放To Space区的对象。对于用可达性分析法搜索不到的对象,GC并不一定会回收该对象。要完全回收一个对象,至少需要经过两次标记的过程:第一次标记:对于一个没有其他引用的对象,筛选该对象是否有必要执行finalize()方法,如果没有执行必要,则意味可直接回收。(筛选依据:是否复写或执行过finalize()方法;因为finalize方法只能被执行一次)。第二次标记:如果被筛选判定位有必要执行,则会放入FQueue队列,并自动创建一个低优先级的finalize线程来执行释放操作。如果在一个对象释放前被其他对象引用,则该对象会被移除FQueue队列。
 

垃圾回收算法:常用垃圾回收算法有:

1)Mark-Sweep 标记清除法  缺点:内存碎片化,不连续,如果gc后申请大的内存会引起full gc 浪费CPU资源

2)Copying 复制法    缺点:浪费内存

3)Mark-compact 标记压缩法   缺点:效率比copying稍慢

注意:GC过程中新生代中采用的是from到to的复制算法,因为一次GC后eden区的对象所剩无几,此时copying剩下的对象到Survivor0或者Survicor1中效率高也不会浪费太多内存,而年老代采用的是Mark-Compact算法,所以整个jvm的GC采用的是分代搜集算法,不同代用不通的算法来进行垃圾回收

三、jvm高级篇
 

JVM对象引用类型:

JVM对象分配:

JVM参数及设置:

“-”

“-X”

-XX“”

JVM调优(OutofMemory 与 stackoverflow):

  

你可能感兴趣的:(JAVA开发)