首先说一下JVM JRE 的一部分,也是java程序最基础的部分,缺了java程序就无法运行,
Java 程序能够跨平台运行完全是因为java对不同的操作系统提供了不同的JVM。
计算机运行的无非就是:指令+数据。指令用来执行方法,数据用于存放数据和对象。
1) JVM 的执行过程:
2)运行时数据区就是我们平时所说的JVM内存模型:JMM
JVM在运行过程中会把它所管理的内存划分成若干不同的数据区域!
1 线程私有:程序计数器、虚拟机栈、本地方法栈 。(Java每开启一个线程就开辟这几块区域,每个线程都是互不干扰的。),随着线程的结束而消失。
2 线程共享:堆、方法区 。
程序计数器:唯一不会发生内存溢出的区域(OOM),指向当前线程正在执行的字节码指令地址行号。
(JAVA是多线程的,意味着线程切换;确保多线程情况下程序能正常运行。)
栈:是一种数据结构,FILO,先进后出。(为什么使用栈:兼容方法的嵌套调用,以及程序调用方式,方法都是后面调用的值返回到当前地方继续执行,个人觉得)
本地方法栈:本地方法栈保存的是native方法的信息,当一个JVM创建线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧(普通一个方法一个栈帧,不再分配内存给本地方法),而是简单的动态链接并直接调用。( 首先说一下本地方法是指由native修饰的方法,一般是用来调用底层操作系统的。)
虚拟机栈:私有模型中,重点关注部分,大小(每个线程)设置(-Xss 1 M 默认大小),虚拟机栈总大小无法设定,因为每个线程都会分配一个块私有内存区域(包含虚拟机栈,本地方法栈,程序计数器);存储当前线程运行需要的数据、指令、返回地址。虚拟机栈会为运行到的每个方法开辟一块栈帧(包含:局部变量表,操作数栈,动态链接,方法出口(返回值)等),(除本地方法之外,本地方法用的是动态链接直接调用方式),当前线程执行的方法就在虚拟机栈的栈顶。局部变量表会持有new 出来对象的引用。
堆:(-Xms;-Xmx;-Xmn) (最小,最大,新生代),JAVA内存模型中,重要关注部分,因为涉及到了内存分配(new 关键字,反射等)与回收算法,收集器等。几乎所有的对象(实例变量(在类中没有final、static修饰的变量),new)都在堆中。
方法区:(永久代(jdk1.7以前)、元空间(jdk1.8))存放 类信息、常量(final) 、静态变量、即时编译后的代码。
3)JVM各版本内存区域的变化
运行时常量池 :Class 文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域。
符号引用, 字面量:String a =“你好”,八种基本类型 int a=1;这个1 就是字面量,声明为final的常量
例子:String a ="ab"; 编译的时候确定不了a的值,所以“ab”是运行时动态加载到常量池的;
final String b ="cd"; final 修饰编译的时候就会把“cd”加载到常量池。
而且字面量加载到常量池与final,static 修饰无关。
JDK1.6 运行时常量池在方法区中
JDK1.7 运行时常量池在堆中
实验证明JDK1.8的常量池在堆中,而方法区只是存了常量的引用:
如下两个图可以证明运行的常量池是在堆中,而方法区中只是存了常量的符合引用,所以前后变化不大。
JDK1.8 去永久代:使用元空间(空间大小只受制于机器的内存)替代永久代
永久代参数 -XX:PermSize;-XX:MaxPermSize =100M 超过100M OOM()
元空间参数 -XX:MetaspaceSize; -XX:MaxMetaspaceSize
永久代是用来存储类信息、常量、静态变量、等数据不是个好主意,很容易发生内存溢出的问题。
对永久代进行调优是很困难的,同时将元空间与堆的垃圾回收进行隔离,避免了永久打引发FullGC和OOM等问题。
jdk1.7以前永久代的内存分配机制和垃圾回收机制跟堆策略基本是一样的,这样管理起来非常麻烦,所以jdk1.8之后就去永久打,方法区就用了元空间,这样方法区就与与堆隔离了。
4)直接内存:JVM规范里面没有规定的区域。
直接内存不是虚拟机运行时数据区的一部分,也不是java虚拟机中规范定义的内存区域。
如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作(零拷贝的应用);
这块内存不受java堆大小的限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常。
避免了在Java 堆和Native 堆中来回复制数据,能够提高效率。
5)站在线程的角度
6)深入理解栈和堆
7)未来的JAVA 技术
模块化:OSGI(动态化、模块化),应用层面就是微服务,互联网发展的方向。
混合语言:多个语言都可以运行在JVM中,google的Kolin成为了Android的官方语言。Scala(Kafka)
多核并行:CPU从高频次转为多核心,多核时代。JDK1.7引入了Fork/Join,JDK1.8提出了lamba表达式(函数式编程天生合适并行运行)。
8)整体JDK1.8 JVM 内存模型图