JVM 介绍
JVM 介绍:
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。
java 源代码编译过程
java 基本数据类型
其它数据类型
JVM虚拟机内存结构
JVM虚拟机将其内存分为程序计数器、虚拟机栈、本地方法栈、java堆、方法区。
程序计数器:
是一块私有的内存空间,比较小。记录我们当前线程执行的行号,在多线程中起作用(线程上下文切换),线程切换的时候通过程序计数器知道在哪一行继续执行。
虚拟机栈:
每个线程都有独立的内存空间,线程相互之间不影响,和java线程同一时间创建,主要用来保存局部变量、部分结果、并参与方法的调用和返回,存放主内存的的副本数据。(虚拟机栈运行时使用一种叫“栈帧”的数据结构保存上下文数据。) 线程不共享
一个方法就是一个栈帧,遵循先进后出原则,当一个方法执行完毕的时候栈针空间就会释放。
栈溢出产生原因:
递归调用方法,栈空间产生了非常多的栈针空间一直没有释放。
栈溢出解决方法:
1:增加栈内存。设置栈内存大小:-Xss256k
2:减少递归深度调用,达到一定条件时进行退出。
栈帧:
一个方法对应一个栈帧内存空间 每个方法都有独立的栈帧内存空间,
栈数据结构: 先进后出销毁
栈帧内部细节结构: 局部变量表、操作数栈、动态链接、方法出口
栈帧 就是每个方法需要的运行时内存空间
1.每个运行时所需要的内存,称作为虚拟机栈
2.每个栈有多个栈帧组成,对应着每次方法调用时占用的内存
3.每个线程只能有一个活动的栈,对应着当前正在执行的方法。
栈帧空间在什么销毁:
1:当我们方法执行结束之后,栈帧空间也会销毁
2:方法抛出异常。
本地方法栈:
本地方法栈和JVM栈发挥的作用非常相似,也是线程私有的,区别是JVM栈为JVM执行Java方法(也就是字节码)服务,而本地方法栈为JVM使用到的Native方法服务。它的具体做法是在本地方法栈中登记native方法,在执行引擎执行时加载Native Liberies.有的虚拟机(比如Sun Hotpot)直接把两者合二为一。也就是 java调用c语言代码 jni技术
虚拟机栈用于管理java函数的调用,本地方法栈用于管理本地方法的调用(用C语言实现的方法)。java 中使用 native 修饰的方法,底层通过C语言编写的。
堆:
所有new的对象、数组都是在堆中分配空间。线程共享的
堆分为:新生代、老年代。
新生代:存放刚刚产生的对象和年轻对象。新生代分为:e'den(对象刚创建时)、survivor space()、1(至少被GC一次)。
堆内存溢出:
在申请内存的时候,内存不足 产生堆内存溢出(java.lang.OutOfMemoryError: Java heap space),设置 -Xmx8m
堆内存泄露:
在申请内存之后,一直无法被 GC 回收,导致可用内存越来越少,称内存泄露(java.lang.OutOfMemoryError: GC overhead limit exceeded),一般运行一段时间之后才会出现该问题。设置 -Xmx3M -Xms3M
方法区(永久区、元数据):
jdk1.8之后为元数据,被JVM中的所有线程共享(独立于java堆的内存空间)。主要保存的是类的元数据(类的信息、常量、静态变量、运行时常量)。GC回收时,只回收永久区中常量池的回收(未被引用的常量),再就是对类元数据的回收。
多个线程共享,存在线程安全性问题。
常量池:
概念:定义一个固定值 (配置文件)
1. class常量池(静态常量池)(定义字面量、符号引用)
2. 运行常量池
3. 字符串常量池 String a1="a"; a字符串存放在常量池中,a1变量存放在栈帧的局部表量表中。
例子:
public class Test001 {
public static void main(String[] args) {
String a1 = "a";
String a2 = "a";
String a3 = new String("a");
String a4 = a3.intern();
String b1 = "b";
String ab1 = "ab";
// 变量引用相加
String ab2 = a1 + b1;
// 常量池
String ab3 = "a" + "b";
System.out.println(a1 == a2);
System.out.println(a1 == a3);
System.out.println(a1 == a4);
System.out.println(ab1 == ab2);
System.out.println(ab1 == ab3);
}
}
输出结果为:
true
false
true
false
true
总结:
a1、a2、b1、ab1、ab3 都是存放在字符串常量池中。
ab2是变量引用相加,变量引用相加底层使用的是StringBuilder的append()方法进行追加的,存放在堆中。
a3是通过new的对象存储在堆中。
a4是存储在字符串常量池中,intern()方法将堆内存空间转化为字符串常量池。
JDK1.6、JDK1.7、JDK1.8 字符串常量池放在jvm什么区域
JDK1.6和以前常量池都是放入方法区(永久区)
JDK1.7 常量池放入到堆中(不合理),GC不能频繁回收非常消耗虚拟机内存。
JDK1.8 只是将字符串常量池放入到堆,其他常量都是放在元空间。
变量存放图如下所示:
Java 内存模型
JVM百度百科地址:https://baike.baidu.com/item/JVM/2902369?fr=aladdin