JVM基础知识

引言:JVM常见面试题:

1、请谈谈你对JVM的理解?java8的虚拟机有什么更新?
2、什么是OOM?什么是StackOverflowError?有哪些方法分析?
3、JVM的常用调优参数你知道哪些?
4、谈谈JVM中,对类加载器你的认识?
5、JVM内存模型以及分区,需要详细到每个区放什么
6、堆里面的分区:Eden、survival from to、老年代各自的特点
7、GC的三种收集算法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方
8、Minor GC与Full GC分别在什么时候发生

JVM体系结构概览

一、类装载器(ClassLoader):

负责加载class文件,class文件在文件开头有特定的文件标识,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定

类装载器

启动类加载器加载的是java程序最底层的一下jar包,放在rt.jar,java的版本在不断的更迭,新的jar包由扩展类加载器进行加载。应用程序类加载器则是加载我们在程序中自己写的类。

几种类加载器
双亲委派机制
public class Obb {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println("o:" + o.getClass().getClassLoader());
        Obb ob = new Obb();
        System.out.println("ob:" + ob.getClass().getClassLoader());
    }
}

如上所示一段代码,Object是jdk自带的,所以它的类加载器应该是根加载器,而ob是我自己定义的,他的加载器理所当然的应该是应用程序类加载器,根加载器返回的是null。

示例

二、本地方法栈和本地方法接口

本地方法栈和本地方法接口
线程start方法的底层实际上是native接口

三、程序计数器

程序计数器

四、方法区(Method Area):

供各线程共享时的内存区域。它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的时规范,在不同虚拟机里头实现时不一样的,最典型的就是永久代(java8以前)和元空间(java8以后)。
实例变量时存放在堆内存的,和方法区无关。

五、栈(Stack):

栈也叫栈内存,主管java程序的运行,是在线程创建时创建,它的生命周期是跟随线程的生命周期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就Over,生命周期和线程一致,是线程私有的。8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存种分配。

栈管运行,堆管存储。

栈存储什么?

栈帧种主要保存三类数据:
本地变量:输入参数和输出参数以及方法内的变量;
栈操作:记录出栈,入栈的操作;
栈帧数据:包括类文件、方法等等。

栈运行原理
栈运行示例图

/**
 * 如下所示,main方法作为程序的入口,首先被作为栈帧压入栈的底部,
 * 然后main方法又调用了sayHello方法,将此方法栈帧压入栈中,
 * 而sayHello方法又不停的自己调用,每次调用方法都会形成一个栈帧压入栈,
 * 而栈的大小是有限的,所以最后会导致栈溢出  StackOverflowError
 *注意StackOverflowError是错误而不是异常
 */
public class StackDemo1 {

    public static void main(String[] args) {
        System.out.println("1");
        sayHello();
        System.out.println("3");
    }

    public static void sayHello() {
        System.out.println("hello");
        sayHello();
    }
}
栈、堆、方法区的关系

在栈中存着对象的引用,指向堆中,在栈中每一个引用存储着的是对象的地址,都会在堆中对应着一个类元数据地址,而堆中的类元数据地址会指向方法区,因为方法区存储着这个对象的结构数据,也就是一个对象的模板。

六、堆(Heap):

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

堆内存的逻辑分区

堆内存的逻辑分区

PS:
幸存者0区又称为S0区/From区
幸存者1区又称为S1区/To区

逻辑内存上看

java堆从GC的角度上还可以细分为:新生代(Eden区、From Survivor区和To Survivor区)和老年代。

物理内存占比
MinorGC的过程
永久带也就是java8的元空间

元空间是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。

七、JVM的参数调节

java7的JVM
java8的JVM

如上图所见,java7和java8对于jvm参数的调整,实际上相差不大,
-Xms表示堆内存的起始值,默认是物理内存的1/64
-Xmx表示堆内存的最大值,默认是物理内存的1/4
-Xmn表示堆中young区设置的值

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

(java8以前的永久代)
-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;
XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

在java8以后,永久代已经被移除,被元空间所代替,元空间本质上和永久代类似。区别在于永久代使用的是JVM的堆内存,但是java8以后元空间并不在虚拟机中而是使用本机物理内存。因此,默认情况下,元空间的大小受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据就不再由maxPerSize控制,而是由系统的实际可用空间来控制。

堆调优简介
public class jvmSwitch {
    public static void main(String[] args) {
        //返回JVM试图使用的最大内存量,默认的是系统内存的1/4左右  3616MB/16G
        long maxMemory = Runtime.getRuntime().maxMemory();
        //返回JVM的内存总量
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("MAX_MEMORY=" + maxMemory + "(字节)" + 
                (maxMemory / 1024 / 1024) + "MB");
        System.out.println("TOTAL_MEMORY=" + totalMemory + "(字节)" + 
                (totalMemory / 1024 / 1024) + "MB");
    }
}
IDEA调节JVM参数
IDEA调节JVM参数
GC收集的详细信息
先把最大内存改成了10MB
OutOfMemoryError
OOM
读取MinorGC日志

八:GC算法

分代收集算法!!!

1、GC是分代收集算法:次数上频繁收集的是Young区、次数上较少收集的是old区,基本不动的是元空间。

总体概述

JVM在进行GC时,并非每次都对三个内存一起回收的,大部分时候回收的都是新生代,因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种时全局GC(major GC or Full GC)

Minor GC和Full GC的区别:
minorGC:只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。
majorGC:指发生在老年带的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但是不是绝对的),Major GC的速度一般要比Minor GC慢上10倍以上(因为老年代比新生代大啊!)。

四大算法:引用计数法、复制算法、标记清除法、标记压缩法

2、引用计数法

引用计数法

引用计数法如上所示,在程序中有一个引用,就+1,但是可能会出现A引用B,B引用A的情况,这样的话循环引用较难处理。

2、复制算法

年轻代中的GC,主要靠的是复制算法,年轻代中分为一个Eden和两个Survivor,默认比例是8:1:1。一般情况下新创建的对象会被分配到Eden区(一些大的对象特殊处理)。因为年轻代中的对象基本上都是朝生夕死,所以年轻代的垃圾回收算法使用的是复制算法,复制算法的思想就是将内存分为两块,每次用其中一块,当这一块内存用完,就将还活着的对象复制到另一块上面,复制算法不会产生内存碎片。

原理

在GC开始的时候,对象只会存在于Eden区和名为"From"的Survivor区,Survivor区"To"是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到"To",而在“From”区中,仍会存活的对象就会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到老年代,没有达到阈值的对象会被复制到"To"区域。经过这次"From",新的"From"就是上次GC前的"To",不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区域被填满,"To"区被填满之后,会将所有对象移动到老年代中。

缺点:
1、浪费了一半的内存
2、如果对象存活率高,可以极端一点,假设是100%存活,那么我们需要将所有的对象都复制一遍,并将所有引用地址重置一遍,复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变得不可忽视。所以要使用复制算法,存活率必须非常低才行,更重要的是,要克服50%内存的浪费。

3、标记清除算法

image.png

1、优缺点
优点:不需要额外的空间
缺点:两次扫描,耗时严重;会产生内存碎片。

当程序运行时,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行。

缺点解释:首先,他的缺点就是效率比较低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。其次,主要缺点则是这种方式清理出来的空间内存事不连续的,因为死亡对象都是随机出现在内存的各个角落的,现在把它们清除之后,内存的布局就会乱七八糟。为了应对这一点,JVM必须维持一个内存的空间列表,这又是一种开销,而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

4、标记整理法/标记压缩法(全称:标记-清除-整理算法)

老年代一般是由标记清除或者是标记清除+标记整理

原理

优缺点:
优点:没有内存碎片
缺点:需要移动对象的成本(耗时长)

效率不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址,从效率上来讲,标记/整理算法要低于复制算法。

5、总结:
内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)
内存整齐度:复制算法=标记整理算法>标记清除算法。
内存利用率:标记整理算法=标记清除算法>复制算法

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

没有最优的算法,只有最合适的算法。======>>>>分代收集算法

年轻代
年轻代的特点是区域相对老年代较小,对象存活率低。这种情况用复制算法回收整理,速度是最快的。
老年代
这种情况存在大量存活率高的对象,复制算法变得不合适,一般是由标记清除与标记整理的混合实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352