JVM是什么,一篇文章带你入门JVM,覆盖JVM面试。

        JVM是(Java Vitual Machine Wava虚拟机) 的缩写。虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。

 

Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。

Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行,如下图所示:

简单来说JVM是用来解析和运行Java程序的。

jdk、jre、jvm是什么关系?

(1)JRE(Java Runtime Environment),也就是java平台。所有的java程序都要在JRE环境下才能运行。

(2)JDK(Java Development Kit),是开发者用来编译、调试程序用的开发包。JDK也是JAVA程序需要在JRE上运行。

(3)JVM(Java Virtual Machine),是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。

JVM原理

(1)JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器,可在上面执行字节码程序。

(2)java编译器只要面向jvm,生成jvm能理解的字节码文件。java源文件经编译成字节码程序,通过jvm将每条指令翻译成不同的机器码,通过特定平台运行。

面试简述回答整理:

        将其把.java源文件进行编译成.class字节码文件,然后类加载子系统将其进行加载。然后进入到运行时数据区,运行时数据区中有:直接内存、方法区、堆、栈、程序计数器、本地方法栈 ,然后去执行引擎  进行解释执行。

方法区:有运行常量池、类Class、静态变量

堆:有对象Object、数组Array

栈:局部变量表、操作数栈、方法出口  八种基本数据数据类型都存这里,引用数据类型存储指向堆内存的引用指针

程序计数器:局部变量表、操作数栈、方法出口  八种基本数据数据类型都存这里,引用数据类型存储指向堆内存的引用指针
本地方法栈

在我们的运行时数据库区还分为了线程私有区线程共享区

线程私有区
        这块区域里的内容是与线程一一对应的,这些与线程对应的数据区会随着线程开始和结束而创建和销毁。

线程共享区
        这块区域里的内容会随着虚拟机启动而创建,随着虚拟机退出而销毁。

        所有通过new创建的对象的内存都在堆中分配,在Java中,堆被划分为三个不同的区域:年轻代(Young)、老年代(Tenured)、永久代(JDK8后叫元空间,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。)、年轻代又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包括内存的分配以及回收。 

一、为什么要分为新生代和老年代?

        根据对象存活的时间来看,有的对象寿命长(老年代中一般保存存活了很久的对象,他们存活率高、没有额外空间对它进行分配担保),有的对象寿命短(年轻代用来存放新近创建的对象,年轻代的特点是对象更新速度快,在短时间内产生大量的"死亡对象")。应该将寿命长的对象放在一个区,寿命短的对象放在一个区。不同的区采用不同的垃圾收集算法。寿命短的区清理频次高一点,寿命长的区清理频次低一点。提高效率。所以就有了新生代和老年代。

        永久代就是JVM的方法区。在这里都是放着一些被虚拟机加载的类信息,静态变量,常量等数据。这个区中的东西比老年代和新生代更不容易回收。
1、永久代用于存放静态文件,如java类,方法等。永久代对垃圾回收没有显著影响,但是有一些应用可能动态生成或者调用一些class.

二、每个年代的GC垃圾清理算法都是什么?
        根据各个年代的特点进行对象分区存储,更便于回收,采用最适当的收集算法:
        新生代:每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

        老年代:因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或者“标记-整理”算法。

        新生代又分为Eden和Survivor (From与To,这里简称一个区)两个区,加上老年代就这三个区。数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候就会触发Minor GC,如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中,并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。

 三、为什么要有Survivor区?
        如果没有Survivor区,那么Eden每次满了清理垃圾,存活的对象被迁移到老年区,老年区满了,就会触发Full GC,Full GC是非常耗时的,将Eden区满了的对象,添加到Survivor区,等对象反复清理几遍之后都没清理掉,再放到老年区,这样老年区的压力就会小很多。即Survivor相当于一个筛子,筛掉生命周期短的,将生命周期长的放到老年代区,减少老年代被清理的次数。

Survivor区又分为s0 和s1 区:

分两个区的好处就是解决内存碎片化。

为什么一个Survivor区不行?假设现在只有一个survivor区,我们来模拟一下流程:
新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。

四、Minor GC、Major GC、Full GC都是什么意思? 触发机制

        Minor GC:清理年轻代空间(包括 Eden 和 Survivor 区域),释放在Eden中所有不活跃的对象,释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区。Survivor区被用来作为Eden及老年代的中间交换区域,当老年代空间足够时,Survivor区的对象会被移到老年代,否则会被保留在Survivor区。

        Major GC:清理老年代空间,当老年代空间不够时,JVM会在老年代进行major gc。

        Full GC:清理整个堆空间,包括年轻代和老年代空间。        

        Minor GC是新生代GC:指的是发生在新生代的垃圾收集动作。由于java对象大都是朝生夕死的,所以Minor GC非常频繁,一般回收速度也比较快。(一般采用复制算法回收垃圾)

        Major GC发生在老年代的GC :基本上发生了一次Major GC 就会发生一次 Minor GC。并且Major GC 的速度往往会比 Minor GC 慢 10 倍。
什么时候发生Major GC?1、 对于一个大对象,我们会首先在Eden 尝试创建,如果创建不了,就会触发Minor GC; 2、 随后继续尝试在Eden区存放,发现仍然放不下; 3、尝试直接进入老年代,老年代也放不下; 4、触发 Major GC 清理老年代的空间; 5放的下成功;放不下 OOM;

        Full GC:System.gc()方法的调用此方法的调用是建议JVM进行触发Full GC,老年代空间不足,方法区空间不足 


五、JVM GC垃圾回收

1.1 什么是垃圾?

        垃圾是指运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。

1.2 什么区域需要进行垃圾回收

        JVM 的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java 堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。(1)

        垃圾收集器在对堆区和方法区进行回收前,首先要确定这些区域的对象哪些可以被回收,哪些暂时还不能回收,这就要用到判断对象是否存活的算法。

作用辩诉:gc要做的三件事:1.哪些内存需要回收? 2.什么时候回收 3.怎么回收

一、哪些对象已经死亡:

1、引用计数法 - 会产生循环引用的问题                2、根搜索算法 -

二、垃圾收集算法

1、标记清除 - 效率低,内存碎片多        2、复制算法 -

3、标记整理算法                                  4、分代收集

三、垃圾收集器

1、Serial                      2、ParNew                     3、Parallel Scavenge

4、Serial Old               5、Parallel Old                6、CMS - Concurrent Mark Sweep

 选择正确的 GC 算法,唯一可行的方式就是去尝试,并找出不合理的地方,一般性的指导原则:

  • 如果想要最小化地使用内存和并行开销,选择Serial GC;
  • 如果系统想要最大化应用程序的吞吐量,CPU 资源都用来最大程度处理业务,选择Parallel GC;
  • 如果系统考虑低延迟有限,想要最小化GC的中断或停顿时间,选择CMS GC;
  • 如果系统内存堆较大,同时希望整体来看平均 GC 时间可控,使用 G1 GC。

对于内存大小的考量:

  • 一般 4G 以上,算是比较大,用 G1 的性价比较高。
  • 一般超过 8G,比如 16G-64G 内存,非常推荐使用 G1 GC。

最后一个:JVM调优

        一般JVM调优,重点在于调整JVM堆大小、调整垃圾回收器,jvm调优的目的是,减少full gc、降低gc停顿时间、提高吞吐量;调优的顺序=“提高吞吐量”>“降低gc停顿时间”;在满足吞吐量的前提下,再降低gc停顿时间;若不能同时满足以上,则选择最适合系统的一种调优结果。

JVM调优常用参数

  • -Xmx1024m:最大堆内存,当物理内存不超过192m时最大堆内存为物理内存的一半,否则为物理内存的四分一
  • -Xms1024m:最小堆内存,一般设置为与-Xmx同等值
  • -XX:+PrintGCDetails:输出gc详细日志
  • -XX:+PrintGCTimeStamps或-XX:+PrintGCDateStamps:输出gc信息时带上时间戳
  • jcmd:专用于查看JVM状态,可以查看正在运行的进程,会显示出进程号

        

    愿这篇文章能够让你入门了解JVM,最后附加一个JVM牛皮普拉斯的图。

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值