JVM 介绍

JVM 介绍:

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

java 源代码编译过程

java 基本数据类型

byte://1字节有符号整数的补码
short://2字节有符号整数的补码
int://4字节有符号整数的补码
long://8字节有符号整数的补码
float://4字节IEEE754单精度浮点数
double://8字节IEEE754双精度浮点数
char://2字节无符号Unicode字符
boolean:boolean数据类型表示一位的信息

其它数据类型

object//对一个Javaobject(对象)的4字节引用
returnAddress//4字节,用于jsr/ret/jsr-w/ret-w指令

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

 

posted @ 2019-06-01 09:47  明天,你好啊  阅读(865)  评论(0编辑  收藏  举报