前言:JVM(Java Virtual Machine,又称ava虚拟机),是指运行java程序的运行环境,是一个可以执行java字节码文件的虚拟机。正是因为jvm所提供的环境,所以java语言才能成为一门跨平台的语言,它将对应的.java文件加载为对应的.class字节码文件,然后翻译为对应平台的机器码。java虚拟机的内存空间大致分为几个部分,分别是:java堆,java栈,方法区,本地方法栈。
堆
- 堆又被称为GC堆,在虚拟机启动的时候就创建,几乎所有的对象的实例化之后都在这进行保存,所线程共享,所有的对象实例化以及数组进行分配内存都在堆上进行分配
- 由于很多对象在进行实例化之后,在使用一次之后就不再使用,所以会由后台线程GC线程来进行自动的内存回收。
- 在堆中定义了一个新对象之后,在栈中会定义一个特殊的变量,这个变量的值的等于首地址在堆中的首地址,在栈中的这个变量就变为了数组的的引用变量。
- 常量池在java7之后,也移在了堆中,所以新创建的字符串也存储在堆内存中
堆的垃圾回收机制
- 由于很多定义对象之后,只使用一次或者很少,由于堆空间是固定的,所以需要定时释放堆中的空间,释放空间的算法主要分下边几种:
- 标记计数法是指每次在创建对象的时候,对这个对象加一个计数器。
- 每当一个地方引用这个对象的时候加一,在使用一次之后,这个计数器减一,当这个计数器为0的时候,清理这个对象。
- 不过这种算法无法解决父类和子类中互相调用的情况,以及循环调用的情况,并且这种算法比较浪费空间以及cpu资源。
- 标记清除算法,顾名思义就是指给每个对象打上标记,标记可以指出哪些对象还能使用,以及哪些对象无法使用。
- 其中标记的算法可以使用引用计数法或者可达性分析算法等来得到哪些对象可以回收,哪些对象可以留着。
- 不过这种标记与清楚的效率比较低下,并且在清理完之后,会留下大量的碎片。
- 标记压缩法是在标记清理法的基础上做了优化,将存活的对象压缩到另一边,而后进行垃圾清理。
- 首先将可用堆可用的空间一分二,每次在清理内存的时候,将当前所用的一半内存上所存活的对象全部复制到另一半内存上,将当前的半区的内存以及碎片化全部清理掉。
- 这种算法只能使用一半的空间,另一半空间无法使用,导致空间利用率低下。。
- 根据堆中的对象的存活周期,将整个堆分为几块,大致可以分为新生代和老年代,新创建的对象一般在新生代中进行创建,如果大对象无法在新生代中找到连续的空间来创建,那么也可以直接在老年代里边创建,在经历过几次GC回收之后,依然存活的对象会被移动到老年区中。
- 其中新生代主要分为Edn区,s0(surriver 0)区,s1(surriver 1)区,其中Edn的比较大,一般新生的对会首先在Edn区进行创建,然后经过几轮的GC之后,将存活的对象移至s0或s1区,在s0区或者s1区存活一段时间之后,会将他们移动到老年区。
- 其中s0和s1区是两个完全等价的,他们直接的切换使用复制算法,每过一段时间,将s0或者s1的内容移动至另一半,并将当前的区清空。
- 老年代的回收所使用的算法为标记压缩算法,因为里边的对象不经常清理,所使用的标记还在可以接受的范围之内。
栈
-
java的栈是一个线程私有的空间,一个栈大致由局部变量表,数据栈,帧数据区组成,栈分为三个部分,基本类型变量区,执行环境上下文,操作指令区
-
在新建一个对象的时候,新建的对象会储存在堆的内存中,在栈中之后存储新建的对象的首地址。
-
在main方法中,可以使用局部变量重名的原因也是因为,引用函数会有自己的一块栈区,在进行运算的时候,不和main函数的栈区在一起,所以可以使用同名的变量。
-
在堆中创建一个对象之后,如果对象进行重新赋值,那么堆中的空间就会被JVM下次不定时GC线程给回收掉。
参考:JAVA中的栈和堆
方法区
- 方法区又称永久区,是所有线程共享的一块区域,保存着通过类加载器所加载的类的信息,主要包括类的字段,方法,定义的常量等,方法区的大小决定了当前程序能够保存的类的数量。
- 由于在运行过程中,在运行前所定义的方法不会再进行变化,所以方法区的大小几乎不变,gc线程这也是方法区被称为永久区的原因。但是如果定义太多的类,导致方法区溢出,会抛出内存溢出的错误。
- jdk1.8已经将方法区改为原数据区,在配置里边可以使用jvm参数来配置方法区的大小
本地方法栈
-
本地方法栈一般是调用其他语言所使用的栈,如果调用的是c语言所编写的,那么这个栈就是c栈。
这种非java语言书写的方法,在java中叫做native method,这种方法- -
一般是用来与java环境外进行交互,或者跟操作系统进行交互的时候使用。
-
native标识符和与其他的标识符进行连用,也可以返回任意类型,也会抛出异常等。
参考:本地方法栈及native方法