JVM

JVM就是Java Virtual Machine(java虚拟机)的缩写,是在现有的平台上,运行一个软件,虚拟出一台接口统一的计算机,因此实现java语言的跨平台特性。

虚拟机本质就是一个进程。模拟平台操作的一个进程。

任何一台计算机,都能部署好jvm,不需要额外购买硬件设施,所以不是什么硬件技术。

JVM是在计算机硬件系统上用软件实现的一台假想机。Java程序在执行时,JVM把字节码解释成机器码。

JVM(Java虚拟机)是运行Java程序必不可少的机制。JVM实现了Java语言最重要的特性:即平台无关性。原理:编译后的Java程序指令并不直接运行在硬件系统的CPU上,而是由JVM执行。JVM屏蔽了与具体平台相关的信息,使Java语言编译程序只需要生成在JVM上运行的目标字节码(.class),就可以在多种平台上不加修改地运行。JVM在执行字节码时,把字节码解释成具体平台上的机器指令执行。

JVM是编译后的JAVA程序(.class文件)和硬件系统之间的接口(javac是收录于JDK中的Java语言编译器,该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行在Java虚拟机的字节码)

Java虚拟机的功能:通过ClassLoader寻找和装载class文件;解释字节码成为指令并执行,提供class文件的运行环境;进行运行期间垃圾回收;提供与硬件交互的平台。

JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area
classloader 把硬盘上的class 文件加载到JVM中的运行时数据区域, 但是它不负责这个类文件能否执行,而这个是执行引擎负责的。

  1. Classloader

作用:装载.class文件
classloader 有两种装载class的方式 (时机):

  1. 隐式:运行过程中,碰到new方式生成对象时,隐式调用classLoader到JVM
  2. 显式:通过class.forname()动态加载

JDK中提供了三个ClassLoader,根据层级从高到低为:

Bootstrap ClassLoader,当运行JVM时,这个类加载器被创建,它负责加载虚拟机的核心类库,如java.lang.*。是所有类加载器的父类。需注意的是这个类加载器不是用java语言写的,而是用C/C++写的。

Extension ClassLoader,主要加载Java的扩展API,也就是%JAVA_HOME%\lib\ext目录下的库类。

Application ClassLoader,主要加载用户机上Classpath设置目录中的库类,一般情况下这是程序中的默认类加载器,也是ClassLoader.getSystemClassLoader() 的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)

JVM加载类的实现方式,我们称为 双亲委托模型:

如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载。

双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。

 

  1. execution engine

作用:执行字节码,或者执行本地方法

 

  1. runtime data area

JVM在运行期间,其对JVM内存空间的划分和分配。JVM在运行时将数据划分为6个区域进行存储。分为堆区、方法区、虚拟机栈、本地方法栈、PC程序计数器、运行时常量池(其空间从方法区中分配,存放的为类中固定的常量信息、方法和域的引用信息)。

PC程序计数器:一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。Java的多线程机制离不开程序计数器,每个线程都有一个自己的PC,以便完成不同线程上下文环境的切换。总结如下:

  1. 程序计数器指定下一条需要执行的指令
  2. 每一个线程独有一个程序计数器
  3. 执行java代码时,PC保存当前指令地址
  4. 执行native方法的时候,PC为空
  5. 不会造成OutOfMemoryError情况

Java虚拟机栈:(存放基本类型和对象引用,存放方法局部变量,执行环境上下文)与 PC 一样,java 虚拟机栈也是线程私有的。每一个 JVM 线程都有自己的 java 虚拟机栈,这个栈与线程同时创建,它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

本地方法栈:与虚拟机栈的作用相似,虚拟机栈为虚拟机执行执行java方法服务,而本地方法栈则为虚拟机使用到的本地方法服务。

堆区:被所有线程共享的一块存储区域,在虚拟机启动时创建,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配。

Java堆在JVM启动的时候就被创建,堆中储存了各种对象,这些对象被自动管理内存系统(Automatic Storage Management System,也即是常说的 “Garbage Collector(垃圾回收器)”)所管理。这些对象无需、也无法显示地被销毁。

垃圾回收主要针对的是堆区的回收,因为栈区的内存是随着线程而释放的。堆区分为三个区:年轻代(Young Generation),年老区(Old Generation),永久代(Permanent Generation,也就是方法区)。

年轻代:对象创建(new)时的对象通常被放在Young(除了一些占据内存比较大的对象),经过一定的Minor GC(针对年轻代的内存回收)还活着的对象会被移动到年老代

年老代:就是上述年轻代移动过来的和一些比较大的对象。Minor GC(FULL GC)是针对年老代的回收

永久代:存储的是final常量,static变量,常量池

-Xmx:最大堆大小

-Xms:初始堆大小

-Xmn:年轻代大小

-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值

年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。

-Xms初始堆大小即最小内存值为10240m

 

JVM的垃圾回收:

1,新生代:(1)所有对象创建在新生代的Eden区,当Eden区满后触发新生代的Minor GC,将Eden区和非空闲Survivor区存活的对象复制到另外一个空闲的Survivor区中。(2)保证一个Survivor区是空的,新生代Minor GC就是在两个Survivor区之间相互复制存活对象,直到Survivor区满为止。
2,老年代:当Survivor区也满了之后就通过Minor GC将对象复制到老年代。老年代也满了的话,就将触发Full GC,针对整个堆(包括新生代、老年代、持久代)进行垃圾回收。
3,持久代:持久代如果满了,将触发Full GC。

Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。注意:finalize不一定被jvm调用,只有当垃圾回收器要清除垃圾时才被调用。

Finalize方法并不是在GC的时候就一定被执行了,finalize只能执行一次,一旦下一次再进入被回收的队列,就不会再执行finalize方法了,直接被标记为第二次,然后下一次GC来的时候被GC。

方法区:(主要用于存放类的信息、常量池、静态变量、编译器编译后的字节码等)方法区和堆区域一样,是各个线程共享的内存区域,它用于存储每一个类的结构信息,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。当开发人员在程序中通过Class对象中的getName、isInstance等方法获取信息时,这些数据都来自方法区。

java提供了一个系统级的线程,即垃圾回收器线程。用来对每一个分配出去的内存空间进行跟踪。当JVM空闲时,自动回收每块可能被回收的内存,GC是完全自动的,不能被强制执行。程序员最多只能用System.gc()来建议执行垃圾回收器回收内存,但是具体的回收时间,是不可知的。

当对象的引用变量被赋值为null,可能被当成垃圾。

垃圾回收程序一般是在堆上分配空间不够的时候会自己进行一次GC,程序员不需要也不能主动释放内存。

JVM将内存区域划分为:方法区、堆、程序计数器、VM Stack(虚拟机栈)、本地方法栈。

其中方法区和堆是线程共享的;VM Stack、本地方法栈和程序计数器都是非线程共享的。

JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) ,   VM Stack(虚拟机栈)和Native Method Stack  (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说知发生在Heap上)的原因。

 

1.Serial收集器 
单线程收集器,收集时会暂停所有工作线程(我们将这件事情称之为Stop The World,下称STW),使用复制收集算法,虚拟机运行在Client模式时的默认新生代收集器。 

2.ParNew收集器 
ParNew 收集器就是Serial的多线程版本,除了使用多条收集线程外,其余行为包括算法、STW、对象分配规则、回收策略等都与Serial收集器一摸一样。对 应的这种收集器是虚拟机运行在Server模式的默认新生代收集器,在单CPU的环境中,ParNew收集器并不会比Serial收集器有更好的效果。 

3.Parallel Scavenge收集器 
Parallel Scavenge收集器(下称PS收集器)也是一个多线程收集器,也是使用复制算法,但它的对象分配规则与回收策略都与ParNew收集器有所不同,它是 以吞吐量最大化(即GC时间占总运行时间最小)为目标的收集器实现,它允许较长时间的STW换取总吞吐量最大化。 

4.Serial Old收集器 
Serial Old是单线程收集器,使用标记-整理算法,是老年代的收集器,上面三种都是使用在新生代收集器。 

5.Parallel Old收集器 
老年代版本吞吐量优先收集器,使用多线程和标记-整理算法,JVM 1.6提供,在此之前,新生代使用了PS收集器的话,老年代除Serial Old外别无选择,因为PS无法与CMS收集器配合工作。 

6.CMS(Concurrent Mark Sweep)收集器 
CMS 是一种以最短停顿时间为目标的收集器,使用CMS并不能达到GC效率最高(总体GC时间最小),但它能尽可能降低GC时服务的停顿时间,这一点对于实时或 者高交互性应用(譬如证券交易)来说至关重要,这类应用对于长时间STW一般是不可容忍的。CMS收集器使用的是标记-清除算法,也就是说它在运行期间会 产生空间碎片,所以虚拟机提供了参数开启CMS收集结束后再进行一次内存压缩。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值