Java JVM原理与调优_动力节点Java学院整理

JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。是运行Java应用最底层部分。

JDK(Java Development kit)

整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具(编译,debug等)和Java基础的类库(rt.jar)。是开发java应用的基础。

JRE(Java Runtime Environment,Java运行环境)

运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。运行java应用的基础。

J2SE(Java 2 Platform,Standard Edition)。

包含那些构成Java语言核心的类。比如:数据库连接、接口定义、输入/输出、网络编程

J2EE(Java 2 Platform,Enterprise Edition)。

Enterprise Edition(企业版) J2EE 包含J2SE 中的类,并且还包含用于开发企业级应用的类。比如:EJB、servlet、JSP、XML、事务控制。

主要JVM

首先,JVM是一套规范。很多公司均实现了各自的虚拟机。常见的有

HotSpot JVM(sun)
Jrockit JVM(BEA公司的JVM,应用于weblogic)
IBM JVM
Apache Harmony

其中,我们常用的是HotSpot JVM.

JVM结构

第一步(编译):

创建完源文件之后,程序会先被编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后 引用,这个有点象make。如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话 报“cant find symbol”的错误。

第二步(运行):

Java类运行的过程大概可分为两个过程:1、类的加载  2、类的执行。

需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。

1、 在 编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将 MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。

2、 (java命令)然后JVM找到AppMain的主函数入口,开始执行main函数

3、 (类加载器)执行过程中,会创建对象。JVM会首先从方法区加载类信息和相关常量,class加载完毕之后,在堆上为对象分配内存,然后调用初始化实例,当然这时候实例保持指向class类型信息,这个信息保存在方法区中。

4、 (执行引擎)调用实例方法时,会根据引用找到对象信息,进而可定位对应的class类型信息,和方法表。

5、 (执行引擎)执行方法时,在虚拟机栈中进行,分配栈帧,随着入栈出栈,完成方法调用操作。

执行引擎

运行Java的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,他要么在执行字节码,要么在执行本地方法。一个线程可能通过解释或者使用芯片级指令直接执行字节码,或者间接通过JIT执行编译过的本地代码。我们上文讲到的main函数,也就是执行引擎的操作入口。

Class文件

实际上,Class文件中方法的字节码流就是有JVM的指令序列构成的。每一条指令包含一个单字节的操作码,后面跟随0个或多个操作数。

iload_0    // 把存储在局部变量区中索引为0的整数压入操作数栈。
iload_1    // 把存储在局部变量区中索引为1的整数压入操作数栈。
iadd         // 从操作数栈中弹出两个整数相加,在将结果压入操作数栈。
istore_2   // 从操作数栈中弹出结果

JVM运行时数据区

1)程序计数器(线程私有)

当前线程所执行的字节码的行号指示器,通过改变这个计数器的值,确定下一条要执行的命令。分支,循环,跳转都需要它的支持。

它是线程私有的,每个线程都有专属于自己的程序记数器,线程之间互不影响,独立存储,保证了线程切换后,可以恢复到原先执行位置。

2)Java虚拟机栈(线程私有)

每个方法的执行,同时都会在虚拟机栈上创建一个栈帧。用于存储局部变量表,操作数栈,方法出口,动态链接等。一个方法的执行周期,同时也就对应着栈帧的出栈入栈操作。有时候方法的递归,会造成大量的栈帧,达到一定的深度,会报StackOverflowError异常。有一点需要说明:在编译器编译Java代码时,就已经在字节码中为每个方法都设置好了局部变量区和操作数栈的数据和大小。并在JVM首次加载方法所属的Class文件时, 就将这些数据放进了方法区。因此在线程调用方法时,只需要根据方法区中的局部变量区和操作数栈的大小来分配一个新的栈帧的内存大小,并堆入Java栈。

局部变量区: 用来存放方法中的所有局部变量值,包括传递的参数。这些数据会被组织成以一个字长(32bit或64bit)为单位的数组结构(以索引0开始)中。其中类 型为int, float, reference(引用类型,记录对象在堆中地址)和returnAddress(一种JVM内部使用的基本类型)的值占用1个字长,而byte, char和shot会扩大成1个字长存储,long,double则使用2个字长。

操作数栈: 用来在执行指令的时候存储和使用中间结果数据。

帧数据区: 常量池的解析,正常方法返回以及异常派发机制的信息数据都存储在其中。

3)本地方法栈(线程私有)

与Java虚拟机栈类似,只不过该区域是为native方法提供服务。

4)方法区(Perm)(线程共享)

存储已被虚拟机加载的类信息,常量,静态变量,即时编译后的代码等数据。包含运行时常量池,用于存放编译器生成的各种字面量和符号引用,这部分内容是在类加载后进入方法区运行时常量池中。

5)堆

堆是整个内存数据区最负责的部分,负责对象的创建。同时,垃圾回收的主要工作也在于此。堆又进一步进行细分,主要是为了满足垃圾回收。

堆的组成

Eden(伊甸园):对象创建的入口。

Survivor Space:用于保存在eden space内存池中经过垃圾回收后没有被回收的对象,也就是“幸存还活着”的对象。

幸存者0区(Survivor 0 space)和幸存者1区(Survivor1 space):当伊甸园的空间用完时,程序又需要创建对象;此时JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象 进行销毁工作。同时将伊甸园中的还有其他对象引用的对象移动到幸存者0区。幸存者0区就是用于存放伊甸园垃圾回收时所幸存下来的JAVA对象。

当将伊甸园中的还有其他对象引用的对象移动到幸存者0区时,如果幸存者0区也没有空间来存放这些对象时,JVM的垃圾回收器将对幸存者0区进行垃圾 回收处理,将幸存者0区中不在有其他对象引用的JAVA对象进行销毁,将幸存者0区中还有其他对象引用的对象移动到幸存者1区。幸存者1区的作用就是用于 存放幸存者0区垃圾回收处理所幸存下来的JAVA对象。

Tenured :对象经过survivor 1 space内存池,每经历过一次垃圾回收,年龄就增加1,超过设定阀值后,被移入终身代,当然也包括由于担保机制移入的对象。对于新生代和老年代,垃圾回收器对其态度不同。发生在新生代的回收频率频繁,大部分对象是“朝生夕死”,收集算法一般采用高效简单的复制算法,也就是上文描述的对象转移操作(Eden->survivor 0,survivor 0->survivor 1)。发生在该区域的垃圾回收为Young GC.对于老年代,由于大部分对象主要为存活率高的对象,垃圾回收器采用”标记-整理“算法。发生在该区域的垃圾回收为FULL GC.

堆相关参数

(影响堆空间划分,进而会影响GC发生频率)JVM调优工作,主要是基于这些参数,进行适当调整管理,达到调整堆内存大小及比例大小,以满足实际业务需求。另外还包括方法区。

-Xms:设置 Java 应用程序启动时的初始堆大小;
-Xmx:设置 Java 应用程序能获得的最大堆大小;
-Xss:设置线程栈的大小;
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例。当堆空间的空闲内存小于这个数值时,JVM 便会扩展堆空间;
-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆;
-XX:NewSize:设置新生代的大小;
-XX:NewRatio:设置老年代与新生代的比例,它等于老年代大小除以新生代大小;
-XX:SurvivorRatio:新生代中 eden 区与 survivor 区的比例;
-XX:MaxPermSize:设置最大的持久区大小;
-XX:TargetSurvivorRatio: 设置 survivor 区的可使用率。当 survivor 区的空间使用率达到这个数值时,会将对象送入老年代。

对象的生命周期

  创建阶段

1、检查指令的参数,是否能在常量池中定位到一个类的符号引用,如果是引用,判断代表的类是否加载,解析和初始化过

2、如果没有加载,则必须进行加载,解析和初始化

3、类加载检查,这时候已经知道所需内存的大小。

4、分配内存。从java堆中划分一块大小确定的内存。支持2种方式,至于选择哪种方式分配内存,与java堆是否规整有关(也就是是否空间空间和使用空间相互交错情况)。1.指针碰撞(分界点的指示器移动);2.空闲列表方式。然而,java堆是否规整,则取决于垃圾收集器的工作方式。此外,在分配内存时还要考虑多线程情况,保证原子性。分配内存的原子性有2种方式进行保证(CAS 和 本地线程分配缓冲-XX +/- UseTLAB)。

5)、分配内存完成后,初始化内存空间(初始化为0)

6、维护对象的对象头信息。如元数据信息,哈希码,GC分代年龄,锁信息,类元指针。

7、调用init方法,按照程序员意愿进行初始化。

<7.1> 从超类到子类对static成员进行初始化;
     <7.2> 超类成员变量按顺序初始化,递归调用超类的构造方法;
      <7.3> 子类成员变量按顺序初始化,子类构造方法调用。

应用阶段

分为强引用、软引用、虚引用、若引用

   不可视阶段;

当一个对象处于不可视阶段,说明我们在其他区域的代码中已经不可以在引用它,其强引用已经消失,例如,本地变量超出了其可视的范围。

   不可到达阶段;

处于JVM对象生命周期不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用,这些对象通常是指所有线程栈中的临时变量, 所有已装载的类的静态变量或者对本地代码接口(JNI)的引用。这些对象都是要被垃圾回收器回收的预备对象,但此时该对象并不能被垃圾回收器直接回收。其 实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但是用户程序不可到达的内存块。

可收集阶段、终结阶段、释放阶段 ;

当一个对象处于可收集阶段、终结阶段与释放阶段时

<1> 回收器发现该对象已经不可达。

<2> finalize方法已经被执行。

<3> 对象空间已被重用。

相关阅读:

JVM(Java虚拟机)简介(动力节点Java学院整理)

以上所述是小编给大家介绍的Java JVM原理与调优_动力节点Java学院整理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 深入JVM剖析Java的线程堆栈

    在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因.在我看来线程堆栈分析技术是Java EE产品支持工程师所必须掌握的一门技术.在线程堆栈中存储的信息,通常远超出你的想象,我们可以在工作中善加利用这些信息. 我的目标是分享我过去十几年来在线程分析中积累的知识和经验.这些知识和经验是在各种版本的JVM以及各厂商的JVM供应商的深入分析中获得的,在这个过程中我也总结出大量的通用问题模板. 那么,准备好了么,现在就把这篇文章加入书签,在后续几周中我会给大家带来这一系列的专

  • JVM(Java虚拟机)简介(动力节点Java学院整理)

    一.概要 1.Java虚拟机(Jvm)是什么? 2.Java虚拟机是用来干什么的? 3.Java虚拟机它的体系结构是什么样子的? 4.Java虚拟机在工作做扮演什么角色? 5.Java虚拟机在运行时数据区? 二.Jvm基础概念 Java虚拟机(Jvm)是可运行Java代码的假想计算机. Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. 在了解Jvm之前,大家如果有兴趣的,也可以先去了解下Java 中的堆和栈. 三.Jvm 我们都知道Java源文件,通过编译

  • Windows下java、javaw、javaws以及jvm.dll等进程的区别

    java  ,javaw   和  javaws 的区别: 首先,所有的这些都是java的启动装置,java.exe经常使用,当使用命令行输出到window的时候,会有java.exe进程,通过任务管理器可以看到.通常 我们执行一些小的java程序的时候会有 java.exe进程在运行.javaw.exe对于我们也比较特殊,我们也能够通过任务管理器看到javaw.exe进程的运行.javaws通常web开启的时候的进程. jvm.dll jvm.dll是一个java虚拟机在windows平台环境

  • Java虚拟机JVM性能优化(一):JVM知识总结

    Java应用程序是运行在JVM上的,但是你对JVM技术了解吗?这篇文章(这个系列的第一部分)讲述了经典Java虚拟机是怎么样工作的,例如:Java一次编写的利弊,跨平台引擎,垃圾回收基础知识,经典的GC算法和编译优化.之后的文章会讲JVM性能优化,包括最新的JVM设计--支持当今高并发Java应用的性能和扩展. 如果你是一个开发人员,你肯定遇到过这样的特殊感觉,你突然灵光一现,所有的思路连接起来了,你能以一个新的视角来回想起你以前的想法.我个人很喜欢学习新知识带来的这种感觉.我已经有过很多次这样

  • Java虚拟机JVM性能优化(三):垃圾收集详解

    Java平台的垃圾收集机制显著提高了开发者的效率,但是一个实现糟糕的垃圾收集器可能过多地消耗应用程序的资源.在Java虚拟机性能优化系列的第三部分,Eva Andreasson向Java初学者介绍了Java平台的内存模型和垃圾收集机制.她解释了为什么碎片化(而不是垃圾收集)是Java应用程序性能的主要问题所在,以及为什么分代垃圾收集和压缩是目前处理Java应用程序碎片化的主要办法(但不是最有新意的). 垃圾收集(GC)的目的是释放那些不再被任何活动对象引用的Java对象所占用的内存,它是Java

  • Java程序员必须知道的5个JVM命令行标志

    本文是Neward & Associates的总裁Ted Neward为developerworks独家撰稿"你不知道5个--"系列中的一篇,JVM是多数开发人员视为理所当然的Java功能和性能背后的重负荷机器.然而,我们很少有人能理解JVM是如何进行工作的-像任务分配和垃圾收集.转动线程.打开和关闭文件.中断和/或JIT编译Java字节码,等等. 不熟悉JVM将不仅会影响应用程序性能,而且当JVM出问题时,尝试修复也会很困难. 本文将介绍一些命令行标志,您可以使用它们来诊断和

  • Java虚拟机JVM性能优化(二):编译器

    本文将是JVM 性能优化系列的第二篇文章(第一篇:传送门),Java 编译器将是本文讨论的核心内容. 本文中,作者(Eva Andreasson)首先介绍了不同种类的编译器,并对客户端编译,服务器端编译器和多层编译的运行性能进行了对比.然后,在文章的最后介绍了几种常见的JVM优化方法,如死代码消除,代码嵌入以及循环体优化. Java最引以为豪的特性"平台独立性"正是源于Java编译器.软件开发人员尽其所能写出最好的java应用程序,紧接着后台运行的编译器产生高效的基于目标平台的可执行代

  • Java JVM原理与调优_动力节点Java学院整理

    JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.是运行Java应用最底层部分. JDK(Java Development kit) 整个Java的核心,包括了Java运行环境(Java Runtime E

  • Java线程安全的常用类_动力节点Java学院整理

    线程安全类 在集合框架中,有些类是线程安全的,这些都是jdk1.1中的出现的.在jdk1.2之后,就出现许许多多非线程安全的类. 下面是这些线程安全的同步的类: vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用.在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的. statck:堆栈类,先进后出 hashtable:就比hashmap多了个线程安全 除了这些之外,其他的集合大都是非线程安全的类和接口. 线程安全的类其方法是同步

  • Java中Object toString方法简介_动力节点Java学院整理

    一.Object类介绍  Object类在Java里面是一个比较特殊的类,JAVA只支持单继承,子类只能从一个父类来继承,如果父类又是从另外一个父类继承过来,那他也只能有一个父类,父类再有父类,那也只能有一个,JAVA为了组织这个类组织得比较方便,它提供了一个最根上的类,相当于所有的类都是从这个类继承,这个类就叫Object.所以Object类是所有JAVA类的根基类,是所有JAVA类的老祖宗.所有的类,不管是谁,都是从它继承下来的. 二.toString方法介绍  一个字符串和另外一种类型连接

  • Java IO流体系继承结构图_动力节点Java学院整理

    Java IO体系结构看似庞大复杂,其实有规律可循,要弄清楚其结构,需要明白两点: 1. 其对称性质:InputStream 与 OutputStream, Reader 与 Writer,他们分别是一套字节输入-输出,字符输入-输出体系 2. 原始处理器(适配器)与链接流处理器(装饰器) 其结构图如下: Reader-Writer体系 1. 基类 InputStream与OutputStream是所有字节型输入输出流的基抽象类,同时也是适配器(原始流处理器)需要适配的对象,也是装饰器(链接流处

  • Java的几个重要版本_动力节点Java学院整理

    java几个重大版本 java从1995年发布到现在,也走过18年了,个人认为,其中几个java版本都肩负着重大使命,影响甚远: jdk1.0 1995年5月23日诞生,Oak语言改名为Java,并提出"Write Once ,Run anywhere": jdk1.2  1999年6月发布,将java划分为J2SE,J2ME,J2EE三大平台: jdk1.4 主要是性能提升,在2000年时候JAVA成为世界上最流行的电脑语言,跟这个版本离不开关系,估计国内还有大量的java应用是运行

  • Java concurrency之AtomicLongArray原子类_动力节点Java学院整理

    AtomicLongArray介绍和函数列表  AtomicLongArray函数列表 // 创建给定长度的新 AtomicLongArray. AtomicLongArray(int length) // 创建与给定数组具有相同长度的新 AtomicLongArray,并从给定数组复制其所有元素. AtomicLongArray(long[] array) // 以原子方式将给定值添加到索引 i 的元素. long addAndGet(int i, long delta) // 如果当前值 =

  • Java动态代理机制详解_动力节点Java学院整理

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息,生成对应的 Class对象: class字节码文件是根据JVM虚拟机规范中规定的字节码组织规则生成的.具体class文件是怎样组织类信息的,可以参考 此博文:深入理解Java Class文件格式系列.或者是Java虚拟机规范. 下面通过一段代

  • Java concurrency之AtomicLong原子类_动力节点Java学院整理

    AtomicLong介绍和函数列表 AtomicLong是作用是对长整形进行原子操作. 在32位操作系统中,64位的long 和 double 变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性.而使用AtomicLong能让long的操作保持原子型. AtomicLong函数列表 // 构造函数 AtomicLong() // 创建值为initialValue的AtomicLong对象 AtomicLong(long initialValue) // 以原子方式设置当前值为ne

  • Java concurrency之公平锁(一)_动力节点Java学院整理

    基本概念 本章,我们会讲解"线程获取公平锁"的原理:在讲解之前,需要了解几个基本概念.后面的内容,都是基于这些概念的:这些概念可能比较枯燥,但从这些概念中,能窥见"java锁"的一些架构,这对我们了解锁是有帮助的. 1. AQS -- 指AbstractQueuedSynchronizer类.     AQS是java中管理"锁"的抽象类,锁的许多公共方法都是在这个类中实现.AQS是独占锁(例如,ReentrantLock)和共享锁(例如,Sem

  • Java数据库连接池之c3p0简介_动力节点Java学院整理

    c3p0是什么 c3p0的出现,是为了大大提高应用程序和数据库之间访问效率的. 它的特性: 编码的简单易用 连接的复用 连接的管理 说到c3p0,不得不说一下jdbc本身,c3p0愿意就是对数据库连接的管理,那么原有的概念还是得清晰:DriverManager.Connection.StateMent.ResultMent. jdbc:java database connective这套API,不用多说,是一套用于连接各式dbms或连接桥接器的api,两个层级:上层供应用方调用api,下层,定义

随机推荐