1.导读:
本篇文章我们主要谈的话题为JVM是什么、能干什么、它的内存结构、内存泄露和内存溢出(00M)
2.JVM是什么:
- JVM是Java Virtual Machine 的缩写,翻译过来叫做Java虚拟机。
- 虚拟机是的是用软件来模拟具有完整硬件功能的、运行在一个完全隔离环境中的计算机系统。
- 要清楚JVM是经过裁剪之后的虚拟机,它是一个用来执行字节码指令集的软件。
- 在Java进程启动时会创建一个Java虚拟机,用来解释执行字节码指令,将字节码指令翻译为机器码,最终由系统调度CPU去执行机器码。
- 不同虚拟机的实现,只要运用符合字节码规范的代码都能做到。
3.JVM能干什么:
- 简单讲,我们在执行一个Java代码的时候离不开JVM。Java代码在执行的时候,先将java代码进行编译,生成 .class字节码文件,在进行运行。在运行的时候就要牵扯将字节码文件如何转化成机器码,进而让CUP去执行。
- 下边用一张图来展示java代码是如何运行的。
- 经过上边图的展示,我们可以知道:在一个Java进程启动之后,就会创建一个Java虚拟机。
它是为了能将编译好的字节码指令转换成机器码让CPU执行,所以起到翻译的作用。
4.类加载:
-
上边谈了在启动一个Java进程的时候,要是这个类没有进行加载则要进行类加载。那么类加载发生的时机和类加载做的事情是什么?
-
类加载时机:
(1)new对象时
(2)读写静态变量时
(3)调用静态方法时
(4)牵扯到父类子类是,要是子类想进行类加载,先对父类进行加载。
(5)java类名启动的入口类 -
类加载干的事情:
(1)把字节码加载到java进程的方法区,里边包含类的信息,方法信息等等。
(2)在堆上生成一个Class类对象,作为方法区类信息的访问入口。
5.JVM内存结构:
- JVM在运行java程序的时候,会将它管理的内存进行区域划分,每个区域都用自己的作用。有的区域的生命周期和虚拟机的创建退出有关,有的区域是和具体的线程相关。下边先展示一下JVM在运行java程序时的区域划分。
1)程序计数器:(线程私有)
- 首先是六个里边占据内存空间最小的一个区域。
- 是六个区域里边唯一不会OOM的区域。
是当前线程的行号指示器,起的是指引作用。
在线程进行时间片轮转的时候,切换出去后还是要切换回来。所以就是记录一下行号,在下次切换回来的时候能找到切换出去的位置。
2)JVM栈:(线程私有)
- JVM栈描述的是
java方法运行时的内存模型.
- 每个方法的执行都会创建一个栈帧结构,用来储存局部变量表、操作数栈、方法出口等。
局部变量表存的是:基本数据类型、对象引用等
- 栈帧:值的是线程每调用执行一个方法的时候都会进行入栈和出栈操作。方法进入时创建栈帧(入栈),方法返回时(出栈)。
它的生命周期和线程的生命周期相关联。
线程在启动时JVM栈会创建,线程在销毁时JVM栈会销毁。- 它会存在两个异常:
(1)StackOverflowError:原因是在线程启动调用方法时,如果一直调用没有返回,调用的深度已经超过了JVM给定的深度,就会出现这样的异常。
实际中,就是在我们写递归的时候,要是没有递归出口,就会出现这种异常。
(2)会出现OOM:原因是在JVM栈扩展时,在无法申请到足够内存的时候就会出现内存溢出异常
3)本地方法栈:(线程私有)
- 看字面意思,就是把上边将的JVM换成了本地方法了。因此它的用途和JVM栈是相似的。
JVM栈是虚拟机执行Java方法服务的,本地方法栈是虚拟机执行本地方法服务.
- 它也可能出现StackOverflowError和OOM异常。原因和上边一样。
4)Java堆(线程共享)
在虚拟机启动的时候,就会自动创建出来
,此时创建出的堆所占据的内存称之为堆的最小值。- 堆是六个里边占据内存模块最大的一块区域,
里边存是所有对象的实例或者数组。
-堆是垃圾回收器管理下最主要的区域,因此叫做“GC堆”。
Java堆在物理上可以不连续,因此Java堆再虚拟机中是能够进行扩展的
。启动虚拟机所开辟的堆的内存叫做堆的最小值(-Xms设置最小值,-Xmx设置最大值)- 要是堆的内存够用,就要进行扩展。
要是已经扩展到能允许的最大值并且在经过GC之后要是还不够用,就会出现OOM。
5)方法区:(线程共享)
- 方法区里边存的是已被虚拟机加载好的
类信息、常量、静态变量、及时编译后的代码数据。
- 里边
存在GC
,会回收常量和类型(无用的)。 - 老版本的JDK里边没有进行回收,就会存在内存泄露。
6)运行时常量区(线程共享)
- 属于方法区的一部分,里边有字符串常量池,用于存放字面量和直接引用。
6.内存泄露和内存溢出:
(1)内存泄露:(Memory Leak):
-
定义:
指进程在运行过程中,对内存进行不断的申请,但是却没有进行及时的释放,导致内存里边存储的无用数据越来越多,进而导致可用内存变得越来越少。
-
形象理解:
有一家店,店的面积是有限的。现在顾客陆续进来,顾客进来后没有点餐,而是在那里一直等。伴随着这种顾客的不停增加,导致店的可用位置越来越少。最后导致店里坐满,其他的顾客不能进来。这样的场景就叫内存泄露,长时间的内存泄露会导致OOM。 -
出现场景:
拿用户登录举例子,在用户进行登录的时候,会自动创建出一个Session并保存。创建Session的时候就要占据内存,但是只要是用户没有点击注销或者退出登录的话,这个Session会一直存在。伴随这这种用户的增多,就会不断的创建Session,导致可用内存不断减少。而前边创建好的Session又没有干事情,就一直在哪里等待。这就是内存泄露,可以看出一两次的内存泄露影响到不是很大,但是无数次的内存泄露会导致OOM。 -
如何解决:
还是拿上边顾客用餐例子寻求解决方法。我们很容易能想出:要是顾客进来超过一定时间不点餐,我们就劝他们去别的地方消费。对应的措施就是:在程序代码上进行优化,规定一个时间,要是在这段时间内存上的数据没有用上,就把它释放掉,并且定时扫描并清理不用的数据。
或者直接重启进程,就会从新进行分配。 -
内存泄露在随着时间的推移势必会导致内存溢出。
(2)内存溢出(Out Of Memory)
定义:
简单的将就是你自己的空间不够别人来用了,就会出现内存溢出。形象理解:
你开的店最多容纳30人同时用餐,但是现在有一个团体他们一共有100人想一块进来,这时你的店不够大,没法一次性容纳这些人同时用餐,这就叫做内存溢出。- 内存泄露可能会导致内存溢出,但是内存溢出却不一定是内存泄露导致的。
出现场景:
上边也说了,在Java虚拟机运行起来之后,线程共享的区域都有可能会出现内存溢出。在堆里边存放数据,要是扩展到他能允许的上线,并且GC之后还是不够用的话,就会导致内存溢出。