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堆和方法区的交互原理
-
为了能让递归方法调用的次数更多一些,应该怎么做?
- 1996年 SUN JDK 1.0 Classic VM——纯解释运行,使用外挂进行JIT
- 1997年 JDK1.1 发布——AWT、内部类、JDBC、RMI、反射
- 1998年 JDK1.2 Solaris Exact VM
- JIT 解释器 混合
- Accurate Memory Management 精确内存管理,数据类型敏感
- 提升 GC 性能
- JDK1.2开始称为Java 2,导致了J2SE J2EE J2ME 的出现
- 加入Swing Collections
- 2000年 JDK 1.3 Hotspot (HotSpot 为Longview Technologies开发 被SUN收购)作为默认虚拟机发布——加入JavaSound API
- 2002年 JDK 1.4 Classic VM退出历史舞台(1996年推出的 Classic VM 到2002年出 JDK 1.4 方才退出)——Assert、正则表达式、NIO、IPV6、日志API、加密类库……
-
2004年 JDK1.5 即 JDK5 、J2SE 5 、Java 5 发布了(很重要的一版),是Java语言的发展史上的又一里程碑事件。为了表示这个版本的重要性,JDK 1.5 更名为 5.0
- 泛型
- 注解
- 装箱
- 枚举
- 可变长参数
- Foreach
-
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。
- 脚本语言支持
- JDBC 4.0
- Java编译器 API
- 2006年11月13日,SUN 公司宣布 Java 全线采纳 GNU General Public License Version 2,从而公开了 Java 的源代码,并建立OpenJDK——HotSpot 成为Sun JDK 和 OpenJDK 中所带的虚拟机。
- 2008 年 Oracle 收购 BEA——得到JRockit VM。
- 2010 年 Oracle 收购 Sun——得到Hotspot,Oracle 宣布在 JDK 8 时整合 JRockit 和 Hotspot,优势互补,在Hotspot基础上,移植JRockit优秀特性。
- 2011年 JDK 7 发布,延误项目推到JDK 8
- G1
- 动态语言增强
- 64位系统中的压缩指针
- NIO 2.0
-
2014年 JDK 8 发布
- Lambda 表达式
- 语法增强
- Java类型注解
- 2016年 JDK 9 发布——模块化
Java的两大基石:Java 语言规范和 JVM 规范
- –Groovy
- –Clojure
- –Scala
JVM的启动过程是怎样的?
也就是说JVM是如何一步步的找到main方法的……简单总结下,Java虚拟机启动的过程:
- 首先使用 Java 命令启动JVM
- 其次进行JVM配置的装载——根据当前路径和系统的版本去寻找jvm.cfg文件,装载配置。
- 之后会根据加载的配置去寻找JVM.dll文件——JVM的主要实现文件。
- 再后,通过该文件去初始化JVM,并获得相关的接口,比如JNIEnv接口,通过该接口实现findClass操作。
- 最后,通过相关接口(JNIEnv……),找到程序里的main方法,即可进入程序……
如图:
介绍一下 JVM 的基本结构,并说出各个模块的功能?
可以结合这个网络上经典的 JVM 结构图来理解 JVM,当总结完毕,再画一个更加详细的结构图(jdk 7 规范的 JVM):
首先,要知道JVM有一个类加载系统(不然我们的类没法执行),也就是传说中的ClassLoader……Class文件(Java编译之后的)通过类加载器被加载到JVM中,而JVM的内存空间是分区的,主要有如图所示几个区:
- 方法区
- Java 堆
- Java 栈
- 本地方法栈(也就是native方法调用)
而类比物理cpu,JVM也需要一个指针来指向下一条指令的地址,就是图中的PC寄存器,紧接着是执行引擎,用来执行字节码,当然还有一个很重要的模块——GC(垃圾回收器)。下面单独总结下各个主要模块:
- PC寄存器
- 方法区
- 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 }
编译之后的具备变量表字节码如下:
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
可以认为Java栈帧里的局部变量表有很多的槽位组成,每个槽最大可以容纳32位的数据类型,故方法参数里的int i 参数占据了一个槽位,而long l 参数就占据了两个槽(1和2),Object对象类型的参数其实是一个引用,o相当于一个指针,也就是32位大小。byte类型升为int,也是32位大小。如下:
相对再看看实例方法:
public int doInstanceMethod(char c, short s, boolean b) {
return 0;
}
编译之后的具备变量表字节码如下:
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
实例方法的局部变量表和静态方法基本一样,唯一区别就是实例方法在Java栈帧的局部变量表里第一个槽位(0位置)存的是一个this引用(当前对象的引用),后面就和静态方法的一样了。