浅谈快速入门JVM

JVM的位置
首先我们来看看 JVM 在我们整个系统的位置:
浅谈快速入门JVM_第1张图片
JVM体系结构图
如果你不能够闭着眼睛画出 JVM 的体系结构图,说明你还没有入门 JVM:
浅谈快速入门JVM_第2张图片
类加载器ClassLoader
我们先来看看一个类加载到 JVM 的一个基本结构:
浅谈快速入门JVM_第3张图片
类的加载、连接与初始化
在Java代码中,Class的加载、连接与初始化过程都是在程序运行期间完成的。Runtime!
浅谈快速入门JVM_第4张图片
类的加载
类的加载指的是将类的.class文件中二进制数据读入到内存中,将其放在运行时数据区内的方法区内,然后再内存中创建一个 java.lang.Class 对象用来封装类在方法区内的数据结构。

双亲委派机制

  1. 类加载器收到类加载的请求;

  2. 把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器;

  3. 启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛出异常,通
    知子加载器进行加载。

  4. 重复步骤三;

Native方法
凡是带了native关键字的,说明 java的作用范围达不到,去调用底层C语言的库!
JNI:Java Native Interface (Java本地方法接口)
凡是带了native关键字的方法就会进入本地方法栈;
Native Method Stack 本地方法栈
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java在诞生的时候是C/C++横行的时候,想要立足,必须有调用C、C++的程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是 在 Native Method Stack 中登记native方法。

程序计数器

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

方法区
方法区主要用于存储类信息、常量池、 静态变量,方法数据、方法代码、符号引用等。

栈(Stack)
栈:后进先出 / 先进后出
队列:先进先出(FIFO : First Input First Output)
浅谈快速入门JVM_第5张图片
堆(Heap)
Java7之前
Heap 堆,一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的,类加载器读取了类文件后,需要把类,方法,常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为
三部分:
新生区 Young Generation Space Young/New
养老区 Tenure generation space Old/Tenure
永久区 Permanent Space Perm
堆内存逻辑上分为三部分:新生,养老,永久(元空间 : JDK8 以后名称)

浅谈快速入门JVM_第6张图片
GC垃圾回收主要是在 新生区和养老区,又分为 轻GC 和 重GC,如果内存不够,或者存在死循环,就会导致OOM。

如果出现 java.lang.OutOfMemoryError:java heap space异常,说明Java虚拟机的堆内存不够,原因
如下:
1、Java虚拟机的堆内存设置不够,可以通过参数 -Xms(初始值大小),-Xmx(最大大小)来调整。
2、代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)或者死循环
浅谈快速入门JVM_第7张图片
了解完基本的堆的信息之后,我们就可以简单学习下关于堆内存调优的说明了!我们是基于 HotSpot 虚
拟机的,JDK1.8;
先看下 JDK 1.7 的 和 1.8 的区别
浅谈快速入门JVM_第8张图片
JDK 1.8 的
浅谈快速入门JVM_第9张图片
堆内存调优
-Xms :设置初始分配大小,默认为物理内存的 “1/64”
-Xmx :最大分配内存,默认为物理内存的 “1/4”
-XX:+PrintGCDetails :输出详细的GC处理日志

GC详解
回顾一下 GC 的作用域
浅谈快速入门JVM_第10张图片
记住GC口诀: 分代收集算法
次数频繁Young区,次数较少Old区,基本不动Perm(永久区)区

什么是复制算法?
浅谈快速入门JVM_第11张图片
原理解释:
年轻代中的GC,主要是复制算法(Copying)
HotSpot JVM 把年轻代分为了三部分:一个 Eden 区 和 2 个Survivor区(from区 和 to区)。默认比例为 8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区,对象在Survivor中每熬过一次Minor GC ,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中,因为年轻代中的对象基本上都是朝生夕死,所以在年轻代的垃圾回收算法使用的是复制算法!复制算法的思想就是将内存分为两
块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片!

标记清除
说明:老年代一般是由标记清除或者是标记清除与标记整理的混合实现
什么是标记清除?
回收时,对需要存活的对象进行标记;
回收不是绿色的对象
标记:从引用根节点开始标记所有被引用的对象,标记的过程其实就是遍历所有的GC Roots ,然后将所有GC Roots 可达的对象,标记为存活的对象。
清除: 遍历整个堆,把未标记的对象清除。

标记压缩
标记整理说明:老年代一般是由标记清除或者是标记清除与标记整理的混合实现。
什么是标记压缩?
在整理压缩阶段,不再对标记的对象作回收,而是通过所有存活对象都像一端移动,然后直接清除边界以外的内存。可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉,如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。标记、整理算法不仅可以弥补 标记、清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价;

标记清除压缩
小总结
内存效率:复制算法 > 标记清除算法 > 标记整理算法 (时间复杂度)
内存整齐度:复制算法 = 标记整理算法 > 标记清除算法
内存利用率:标记整理算法 = 标记清除算法 > 复制算法

可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记整理算法相对来说更平滑一些 , 但是效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记清除多了一个整理内存的过程。

你可能感兴趣的:(java,jdk,jvm)