Java虚拟机(JVM)一种用于计算机设备的规范,可用不同的方式(软件或硬件)加以实现。编译虚拟机的指令集与编译微处理器的指令集非常类似。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
Java虚拟机(JVM)是可运行Java代码的假想计算机。只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该系统上运行。
Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有相应的指令系统。
Java虚拟机规范定义了一个抽象的——而非实际的——机器或处理器。这个规范描述了一个指令集,一组寄存器,一个堆栈,一个“垃圾堆”,和一个方法区。一旦一个Java虚拟机在给定的平台上运行,任何Java程序(编译之后的程序,称作字节码)都能在这个平台上运行。Java虚拟机(JVM)可以以一次一条指令的方式来解释字节码(把它映射到实际的处理器指令),或者字节码也可以由实际处理器中称作just-in-time的编译器进行进一步的编译。
引用计数法就是给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的,可以当做垃圾收集。这种方法实现起来很简单而且优缺点都很明显。
优点 执行效率高,程序执行受影响较小
缺点 无法检测出循环引用的情况,导致内存泄露
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
那么什么对象可以作为GCRoot?
该算法分为“标记”和“清除”两个阶段:标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。它是最基础的收集算法,效率也很高,但是会带来两个明显的问题:
为了解决效率问题,我们开发出了复制算法。它可以将内存分为大小相同的两块,每次使用其中的一块。当第一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
简单来说就是该对象分为对象面以及空闲面,对象在对象面上创建,对象面上存活的对象会被复制到空闲面,接下来就可以清除对象面的内存。
这种算法的优缺点也比较明显
**优点:**解决碎片化问题,顺序分配内存简单高效
**缺点:**只适用于存活率低的场景,如果极端情况下如果对象面上的对象全部存活,就要浪费一半的存储空间。
为了解决复制算法的缺陷,充分利用内存空间,提出了标记整理算法。该算法标记阶段和标记清除一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
当前虚拟机的垃圾收集都采用分代收集算法,这种算法就是根据具体的情况选择具体的垃圾回收算法。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
方法区在JDK1.8之后把名字改成了“Metaspace",可以翻译成"元数据空间",这是一块线程共享的内存空间,主要用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
Java虚拟机规范将方法区描述为堆的一个逻辑部分,但它有一个别名”Non-Heap 非堆“ 用于与Java堆区分。
很多人愿意把方法区成为”永久代“,本质上两者并不等价。HotSpot虚拟机 团队选择把GC分代收集扩展至方法区,或者说使用永久带实现方法区而已。
永久代容易遇到内存溢出问题(HotSpot永久代有-XX:MaxPermSize上限)
当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常
运行时常量池也是方法区的一个部分,主要用于存放编译器生成的各种字面量和符号引用。类加载后会进入方法区的运行时常量池
当常量池无法再申请到内存空间时会抛出OutOfMemoryError异常
虚拟机栈是线程私有的内存空间,其生命周期与线程相同,是用于Java方法执行的内存模型。方法执行时会创建栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等。
线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
虚拟机栈动态扩展时无法申请到足够的内存,抛出OutOfMemoryError异常
其发挥的作用和存在的问题与Java虚拟机栈类似,这里主要说一下其区别。其区别主要是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机使用到Native方法服务。
知识点:Hotspot虚拟机中将本地方法栈与虚拟机栈合二为一。
Java堆是虚拟机中占内存最大的一块内存空间,是所有线程共享的内存区域,当虚拟机启动的时候就会创建。它的作用主要是存放对象实例,几乎所有的对象实例都在这里分配内存。我们来看下Java虚拟机规范是怎么描述它的:
所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配到堆上也渐渐变得不是那么”绝对“了。
这一块内存空间也是垃圾收集器管理的主要区域,所以有时候也被称为”GC堆“。
如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
程序计数器是线程私有的一块占用较小的内存空间,主要用于记录当前线程执行到哪里了。而且这也是唯一一个没有内存溢出的区域。
除了以上几块内存空间外,还有一块内存空间就是直接内存,它不属于运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分频繁使用也可能导致OutOfMemoryError异常。NIO可以使用Native函数库直接分配堆外内存空间。
1、-Xmixed mixed mode execution (default)
混合模式执行
2、-Xint interpreted mode execution only
解释模式执行
3、-Xbootclasspath:
set search path for bootstrap classes and resources
设置zip/jar资源或者类(.class文件)存放目录路径
3、-Xbootclasspath/a:
append to end of bootstrap class path
追加zip/jar资源或者类(.class文件)存放目录路径
4、-Xbootclasspath/p:
prepend in front of bootstrap class path
预先加载zip/jar资源或者类(.class文件)存放目录路径
5、-Xnoclassgc disable class garbage collection
关闭类垃圾回收功能
6、-Xincgc enable incremental garbage collection
开启类的垃圾回收功能
7、-Xloggc: log GC status to a file with time stamps
记录垃圾回日志到一个文件。
8、-Xbatch disable background compilation
关闭后台编译
9、-Xms set initial Java heap size
设置JVM初始化堆内存大小
10、-Xmx set maximum Java heap size
设置JVM最大的堆内存大小
11、-Xss set java thread stack size
设置JVM栈内存大小
12、-Xprof output cpu profiling data
输入CPU概要表数据
13、-Xfuture enable strictest checks, anticipating future default
执行严格的代码检查,预测可能出现的情况
14、-Xrs reduce use of OS signals by Java/VM (see documentation)
通过JVM还原操作系统信号
15、-Xcheck:jni perform additional checks for JNI functions
对JNI函数执行检查
16、-Xshare:off do not attempt to use shared class data
尽可能不去使用共享类的数据
17、-Xshare:auto use shared class data if possible (default)
尽可能的使用共享类的数据
18、-Xshare:on require using shared class data, otherwise fail.
尽可能的使用共享类的数据,否则运行失败