JVM系类的文章全部转载自:http://www.cnblogs.com/kubixuesheng/p/5199200.html

特别在此声明。那位博主写的真的很好 ,感谢!!

 

 

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下:

  • JVM的历史
  • JVM的运行流程简介
  • JVM的组成(基于 Java 7)
  • JVM调优参数:-Xmx和-Xms
  • 逃逸分析(DoEscapeAnalysis )的概念——JVM栈上分配实验
  • JVM中client模式(-client)和server模式(-server)的区别
  • 查看GC日志的方法
  • 使用idea对JVM进行参数输入
  • Java栈,Java堆和方法区的交互原理
  • 为了能让递归方法调用的次数更多一些,应该怎么做?


 

  当今——截止本文总结之前,使用最为广泛的 JVM 为 HotSpot(HotSpot 为Longview Technologies开发,被SUN收购),而 JVM 本质上是使用软件来模拟 Java 的字节码的指令集,类比 VMWare,Visual Box……他们都是使用软件去模拟物理CPU的指令集。
  所谓读史使人明智,先简单回顾和了解下 JVM 与 Java 的发展史
  1. 1996年 SUN JDK 1.0 Classic VM——纯解释运行,使用外挂进行JIT
  2. 1997年 JDK1.1 发布——AWT、内部类、JDBC、RMI、反射
  3. 1998年 JDK1.2 Solaris Exact VM
    1. JIT 解释器 混合
    2. Accurate Memory Management 精确内存管理,数据类型敏感
    3. 提升 GC 性能
    4. JDK1.2开始称为Java 2,导致了J2SE J2EE J2ME 的出现
    5. 加入Swing Collections
  4. 2000年 JDK 1.3 Hotspot (HotSpot 为Longview Technologies开发 被SUN收购)作为默认虚拟机发布——加入JavaSound API
  5. 2002年 JDK 1.4 Classic VM退出历史舞台(1996年推出的 Classic VM 到2002年出 JDK 1.4 方才退出)——Assert、正则表达式、NIO、IPV6、日志API、加密类库……
  6. 2004年 JDK1.5 即 JDK5 、J2SE 5 、Java 5 发布了(很重要的一版),是Java语言的发展史上的又一里程碑事件。为了表示这个版本的重要性,JDK 1.5 更名为 5.0 
    1. 泛型
    2. 注解
    3. 装箱
    4. 枚举
    5. 可变长参数
    6. Foreach
  7. 2005年 Java SE 6、JDK 1.6、JDK 6 发布,JavaOne 大会召开,SUN 公司公开 Java SE 6 此时 Java 的各种版本已经更名以取消其中的数字“2”——J2EE更名为Java EE, J2SE更名为Java SE,J2ME更名为Java ME。
    1. 脚本语言支持
    2. JDBC 4.0
    3. Java编译器 API
  8. 2006年11月13日,SUN 公司宣布 Java 全线采纳 GNU General Public License Version 2,从而公开了 Java 的源代码,并建立OpenJDK——HotSpot 成为Sun JDK 和 OpenJDK 中所带的虚拟机。
  9. 2008 年 Oracle 收购 BEA——得到JRockit VM
  10. 2010 年 Oracle 收购 Sun——得到Hotspot,Oracle 宣布在 JDK 8 时整合 JRockit 和 Hotspot,优势互补,在Hotspot基础上,移植JRockit优秀特性。
  11. 2011年 JDK 7 发布,延误项目推到JDK 8
    1. G1
    2. 动态语言增强
    3. 64位系统中的压缩指针
    4. NIO 2.0
  12. 2014年 JDK 8 发布
    1. Lambda 表达式
    2. 语法增强
    3. Java类型注解
  13. 2016年 JDK 9 发布——模块化

 


 

 

  Java的两大基石:Java 语言规范和 JVM 规范

  Java 语言规范:规定了语法、变量、类型、文法,Java 语言规范定义了什么是Java语言
  JVM 规范:规范了 Class 文件类型、运行时数据、帧栈、虚拟机的启动、虚拟机的指令集,JVM规范 主要定义二进制 class文件和 JVM指令集等,且需要明确的问题是 Java语言和JVM相对独立,不论何种语言,但凡符合了JVM规范,那么都可以在JVM上运行,比如:
  • –Groovy
  • –Clojure
  • –Scala

  


 

  JVM的启动过程是怎样的?

  也就是说JVM是如何一步步的找到main方法的……简单总结下,Java虚拟机启动的过程:

  1. 首先使用 Java 命令启动JVM
  2. 其次进行JVM配置的装载——根据当前路径和系统的版本去寻找jvm.cfg文件,装载配置
  3. 之后会根据加载的配置去寻找JVM.dll文件——JVM的主要实现文件。
  4. 再后,通过该文件去初始化JVM并获得相关的接口,比如JNIEnv接口,通过该接口实现findClass操作。
  5. 最后,通过相关接口(JNIEnv……),找到程序里的main方法,即可进入程序……

  如图:

JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html

 


 

  介绍一下 JVM 的基本结构,并说出各个模块的功能?

  可以结合这个网络上经典的 JVM 结构图来理解 JVM,当总结完毕,再画一个更加详细的结构图(jdk 7 规范的 JVM):

JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html

  首先,要知道JVM有一个类加载系统(不然我们的类没法执行),也就是传说中的ClassLoader……Class文件(Java编译之后的)通过类加载器被加载到JVM中,而JVM的内存空间是分区的,主要有如图所示几个区:

  • 方法区
  • Java 堆
  • Java 栈
  • 本地方法栈(也就是native方法调用)

  而类比物理cpu,JVM也需要一个指针来指向下一条指令的地址,就是图中的PC寄存器,紧接着是执行引擎,用来执行字节码,当然还有一个很重要的模块——GC(垃圾回收器)。下面单独总结下各个主要模块:

  • PC寄存器
  Java程序里的每个线程都拥有一个PC寄存器,线程私有的。每当线程启动时PC寄存器就创建了,它是一个指针,总是用来指向下一条指令的地址,让程序知道下一步需要做啥,且执行本地方法时,PC的值为undefined(未定义)
  • 方法区
  保存JVM装载的类的信息,比如类型的常量池、类中的字段,类中的方法信息、方法的字节码(bytecode)等,注意这不是绝对的!!!比如:JDK 6 时,String等字符串常量的信息是置于方法区中的,但是到了JDK 7 时,已经移动到了Java堆。所以,方法区也好,Java堆也罢,到底详细的保存了什么,其实没有具体定论,要结合不同的JVM版本来分析,因为技术是发展的!不过一般认为,方法区就是保存了JVM装载的类的信息。通常方法区和永久区(Perm)关联在一起,永久区是JVM里很常见的一个概念,它保存了相对来说比较稳定的数据……之后再总结。
  • Java堆
  Java堆是和程序开发密切相关的一块内存区间,可以说,应用系统的对象都保存在Java堆中,所有线程共享Java堆,也就是说Java堆是全局共享的,从GC角度看,对使用了分代算法的GC来说,必须堆也是对应分代的,因为Java堆是GC的主要工作区间……比如:如下图,分代的堆:
JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html
  首先有个伊甸园(eden代)——是对象出生的地方,还有s0,s1使用复制算法……以后详细总结。最后还有一个老年代tenured(长期占有的,终身的)——年龄比较大的对象。
  • Java栈
  Java栈和Java堆是完全不一样的,上面说Java堆是全局共享(all线程访问)的,而Java栈是线程私有的,Java栈由一系列的帧组成(因此Java栈也叫做帧栈),栈众所周知是先进后出(FILO)的数据结构,Java栈也不例外,Java栈中的每个帧都保存一个方法调用的局部变量、操作数栈、指向常量池的指针等,且每一次方法调用都会创建一个帧,并压栈
  下面首先分析下Java栈里很重要的一个概念——局部变量表,该表不仅仅只是方法里的局部变量,而是更加宽泛的包含了方法的参数以及局部变量,当方法调用时,会在Java栈里创建一个帧,帧里的局部变量表保存了方法的参数和局部变量。如下一个静态方法
1 public class Demo {
2     public static int doStaticMethod(int i, long l, float f, Object o, byte b) {
3         return 0;
4     }
5 }

  编译之后的具备变量表字节码如下:

JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html
1     LOCALVARIABLE i I L0 L1 0
2     LOCALVARIABLE l J L0 L1 1
3     LOCALVARIABLE f F L0 L1 3
4     LOCALVARIABLE o Ljava/lang/Object; L0 L1 4
5     LOCALVARIABLE b B L0 L1 5
6     MAXSTACK = 1
7     MAXLOCALS = 6
JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html

  可以认为Java栈帧里的局部变量表有很多的槽位组成,每个槽最大可以容纳32位的数据类型,故方法参数里的int i 参数占据了一个槽位,而long l 参数就占据了两个槽(1和2),Object对象类型的参数其实是一个引用,o相当于一个指针,也就是32位大小。byte类型升为int,也是32位大小。如下:

JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html

  相对再看看实例方法:

    public int doInstanceMethod(char c, short s, boolean b) {
        return 0;
    }

  编译之后的具备变量表字节码如下:

JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html
1    L1
2     LOCALVARIABLE this LDemo; L0 L1 0
3     LOCALVARIABLE c C L0 L1 1
4     LOCALVARIABLE s S L0 L1 2
5     LOCALVARIABLE b Z L0 L1 3
6     MAXSTACK = 1
7     MAXLOCALS = 4
JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html

  实例方法的局部变量表和静态方法基本一样,唯一区别就是实例方法在Java栈帧的局部变量表里第一个槽位(0位置)存的是一个this引用(当前对象的引用),后面就和静态方法的一样了。

相关文章:

  • 2021-07-04
  • 2021-12-29
  • 2021-12-24
  • 2021-12-24
  • 2022-12-23
  • 2022-12-23
  • 2021-07-29
  • 2021-05-20
猜你喜欢
  • 2021-10-27
  • 2021-04-06
  • 2021-07-14
  • 2022-02-17
  • 2022-02-12
相关资源
相似解决方案