jvm基础必备

目录

前言

硬件(多cpu)的效率与一致性

jvm内存模型

一、jvm结构

1.1 整体结构

1.2 内存区

1 堆

2 非堆

二、垃圾回收

2.1 如何检测

2.2 如何回收

2.3 垃圾回收分类

三、jvm工具


前言

硬件(多cpu)的效率与一致性

如上图所示,基于高速缓存的存储交互很好的解决了处理器与内存之间的矛盾,也引入了新的问题:缓存一致性问题。在多处理器系统中,每个处理器有自己的高速缓存,而他们又共享同一块内存(下文成主存,main memory 主要内存),当多个处理器运算都涉及到同一块内存区域的时候,就有可能发生缓存不一致的现象。为了解决这一问题,需要各个处理器运行时都遵循一些协议,在运行时需要将这些协议保证数据的一致性。这类协议包括MSI、MESI、MOSI、Synapse、Firely、DragonProtocol等。

 

jvm内存模型

虚拟机内存模型中定义的访问操作与物理计算机处理的基本一致!

如上图所示,Java中通过多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存区域main memory,而每个线程又单独的有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数)。

 

一、jvm结构

1.1 整体结构

整体如下图所示:

 

如图所示,JVM主要包括四个部分:

1.类加载器(ClassLoader):在JVM启动时或者在类运行时将需要的class加载到JVM中。(右图表示了从java源文件到JVM的整个过程,可配合理解。 关于类的加载机制,可以参考http://blog.csdn.net/tonytfjing/article/details/47212291

2.执行引擎:负责执行class文件中包含的字节码指令(执行引擎的工作机制,这里也不细说了,这里主要介绍JVM结构);

3.内存区(运行时数据区):是在JVM运行的时候操作所分配的内存区。

4.本地方法接口:主要是调用C或C++实现的本地方法及返回结果。

 

1.2 内存区

如图所示,内存区包括以下几个部分:

  • 方法区(Method Area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分, 但它却有个别名non-heap(非堆),所以大家不要搞混淆了。方法区还包含一个运行时常量池。jdk1.8以前使用永久带来实现,jdk1.8开始使用元数据区(meta space)来实现。

  • 堆(Heap):存储java实例或者对象的地方,这块是GC的主要区域。从存储的内容我们可以很容易知道,方法区和堆是被所有java线程共享的。
  • 栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是现成私有的。
  • 本地方法栈(Native Method Stack):和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
     
  • 程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。

1 堆

年轻代:是所有新对象产生的地方。年轻代被分为3个部分——Eden区和两个Survivor区(From和to)。当Eden区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中一个survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把它们转移到另一个survivor区(假设为to区)。这样在一段时间内,总会有一个空的survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。需要注意,Survivor的两个区是对称的,没先后关系,from和to是相对的。使用copy算法进行内容回收。

年老代:在年轻代中经历了N次回收后仍然没有被清除的对象,就会被放到年老代中,可以说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。回收算法基本是标记-清除(mark-sweep)或者标记-压缩(mark-compact)。

 

2 非堆

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

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

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

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

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

hotspot jdk8中移除了永久带以后的内存结构:

 

 

二、垃圾回收

垃圾收集器一般必须完成两件事:检测出垃圾;回收垃圾。

2.1 如何检测

引用计数法:给一个对象添加引用计数器,每当有个地方引用它,计数器就加1;引用失效就减1。

可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java栈中引用的对象、方法区常量池中引用的对象、本地方法中引用的对象等。

2.2 如何回收

1.标记-清除(Mark-sweep)
最基础的垃圾收集算法,缺点是效率不高,造成内存碎片

2.复制(Copying)
算法简单,缺点是内存缩小一半

3.标记-整理(Mark-Compact)
适合老年代

4.分代收集算法

这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
新生代:copy算法
老年代:标记清除或者标记-整理

 

2.3 垃圾回收分类

       1、大多数情况下,对象在新生代Eden区分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC
  2、大对象直接进入老年代,所谓大对象是指,需要大量连续内存空间的java对象,虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在Old老年代分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制
  3、长期存活的对象将进入老年代,虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,对象在Survivor区中每熬过一次MinorGC,年龄就增加1岁,默认增加到15岁时改对象就会晋升到老年代,晋升到老年代的年龄阈值,可以通过-XX:MaxTenuringThreshold设置。虚拟机并不是永远要求对象年龄必须达到设置的值才能晋升到老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

 

三、jvm工具


四、参考文章
JVM内存结构,为什么需要GC?
深入理解JVM—JVM内存模型
JVM的方法区和永久代的关系
BATJ面试必会|Jvm 虚拟机篇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章