首先什么是JVM?为什么要引入JVM呢?

JVM:Java Virtual Machine(Java虚拟机)的缩写,我们可以把jvm理解为一个虚拟的机器,它可以按照需要加载的字节码文件,通过虚拟引擎解释字节码,将其翻译成CPU可以识别的指令。
那么问题来了,为什么要引入jvm?
通俗的讲,为了可以让java跨平台。因为在java中,当编译成字节码文件后,操作系统是无法直接执行的,因为不识别,所以才会引入jvm,由jvm负责加载字节码文件,并在jvm中解释运行,将其翻译为CPU可以识别的指令。

jvm的位置: jvm上承开发语言,下接操作系统,并且与硬件没有直接交互。

java为什么要引入空接口 为什么要引入jvm_JVM


jvm的体系结构:

java为什么要引入空接口 为什么要引入jvm_java_02

jvm逻辑空间:堆、栈、方法区、本地方法区、程序计数器

我们顺着jvm的逻辑结构开始学习

(一)堆

1.1 堆(heap):一个JVM中只存在一个堆内存,它的大小是可以调节的。类加载器读取类文件后,一般会把类、方法、常量、引用类型的真实对象放入堆中。(堆里面放的是对象)

1.2堆的三个区域

新生区(伊甸园区)Eden space

养老区(老年代)

永久区(元空间)

java为什么要引入空接口 为什么要引入jvm_java为什么要引入空接口_03


GC(垃圾回收)主要在伊甸园区、养老区

假设堆内存不够会抛出OOM(java.lang.outofMemorryError)

1.2.1新生区:类的诞生甚至死亡。

又可分为:伊甸园区(所有的对象都是从这个区域new出来的) 、幸存区Survivor Space(from区、to区)

1.2.2养老区 :当一个对象经历15次GC 后还没有死,就会进入养老区。

默认是15次,可以修改-xx:MaxTenuringThreshold=5(通过这个参数调节进入老年代的时间)。

1.2.2永久区:这个区域是常驻内存的,用来存放JDK自身携带的class对象、interface元数据、存储jjava运行时的环境或类信息。注意:这个区域不存在垃圾回收。关闭jvm虚拟机就会释放这个区域的内存。

OOM

1.3什么是OOM?
OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,来源于java.lang.OutOfMemoryError。看下关于的官方说明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是说,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
1.4为什么会OOM?
内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。
1.5抛出OOM 的几种情况
1.5.1 java.lang.out.of.MemoryError:Java heap space
抛出这个表示java堆内存溢出,这种情况最常见,一般由于内存泄露或者堆大小设置不当引起的。
内存泄露(解决方法):可通过内存监控软件查找程序中的泄露代码。
堆大小设置不当(解决方法):可以通过虚拟机参数-Xms、-Xmx修改。
1.5.2 java.lang.outofMemory:PermGen space
抛出这个表示java永久代溢出,也就是方法区溢出。一般由于出现大量class或jsp页面,或采用cglib等反射机制的情况、除此之外过多的常量尤其是字符串也会导致方法区溢出。出现这种情况我们可以通过改变方法区的大小来解决,使用类似:
-XX:PermSize=64m
-XX:MaxPermSize=256m
除了上面两种还有一种较为常见的java内存溢出(java虚拟机栈溢出)java.lang.StackOverFlowError
抛出这个,一般是由于程序中存在死循环或深度递归造成的,栈的大小设置太小也会导致。出现这种情况可以通过虚拟机参数-Xss来设置栈的大小来解决。

GC

1.6什么是GC?

GC:也叫垃圾回收机制,就是释放垃圾所占的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆内长时间没有使用或已经死亡的对象清理回收。GC可以分为:轻量级GC、重量级GC。

1.7为什么会有GC?

内存处理是编程人员最容易出现问题的地方,忘记或错误的回收会导致程序或系统不稳甚至崩溃,而GC可以自动监测对象是否超过作用域而达到自动回收内存的目的。

1.8GC最常用的算法

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

1.8.1引用计数法:给每一个对象加一个计数器,当有地方引用该对象计数器加一,没有引用为0,进行垃圾回收的时候首先清除计数器为0的部分。

缺点:不能解决循环问题,过于繁繁琐。

1.8.2复制算法:主要在新生代进行(谁空谁是to)

java为什么要引入空接口 为什么要引入jvm_java_04


每次GC都会将Eden space中活着的对象移到幸存区中(一旦Eden被GC后是空的)

当一个对象经历15次GC后还没有死,就会进入养老区。

java为什么要引入空接口 为什么要引入jvm_JVM_05


优点:没有内存碎片

缺点:浪费内存,总是有一半空间是空的。

1.8.3标记清除算法

java为什么要引入空接口 为什么要引入jvm_JVM_06


优点:不需要额外空间

缺点:两次扫描严重浪费时间,会产生内存碎片。

1.8.4标记压缩算法

java为什么要引入空接口 为什么要引入jvm_java为什么要引入空接口_07


优点:没有内存碎片

总结:

内存效率:复制算法>标记清除算法>标记压缩算法

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

难道就没有最优算法吗?
没有最好的算法,只有最适合的算法!!!
年轻代:复制算法 存活率低
老年代:标记压缩算法+标记清除算法混合使用 区域大存活高。