Java SE体系架构

虚拟机的发展
- HotSpot VM
目前适用范围最广的Java虚拟机 - JRocket VM
号称“世界上最快的Java虚拟机” - J9 VM
- Dalvik VM
未来的Java技术
- 模块化
- 混合语言
- 多核并行
- 丰富语法
- 64位
- 更强的垃圾回收
运行时数据区域
- 定义
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域 - 类型
- 程序计数器 (线程私有)
- 虚拟机栈(线程私有)
- 本地方法堆(线程私有)
- Java堆(线程共享)
- 方法区(运行时常量池,线程共享)
- 直接内存

程序计数器
是指向当前线程正在执行的字节码指令的地址(行号)(面试点)
为什么需要程序计数器(面试点)
- Java是多线程的,意味着存在线程切换
- 确保多线程情况下的程序正常执行
运行时数据区:栈

数据结构
- 入口和出口只有一个
- 入栈
- 出栈
特点(面试点)
- 先进后出(FILO)
为什么JVM要使用栈(面试点)
因为非常符合Java中方法间的相互调用
public class StackFilo {
public static void main(String[] args)
{
A(); //A()->B()->C()
}
public static void A(){
System.out.println("A开始");
//此处省略100行代码
B();//调用B方法
System.out.println("A结束");
}
public static void B(){
System.out.println("B开始");
//此处省略100行代码
C();//调用C方法
System.out.println("B结束");
}
public static void C(){
System.out.println("C开始");
//此处省略100行代码
System.out.println("C结束");
}
}
//log
A开始
B开始
C开始
C结束
B结束
A结束
虚拟机栈
- 定义
存储当前线程运行方法所需的数据、指令、返回地址 - 栈帧
每个方法在执行的同时都会创建一个栈帧
栈帧还可以划分
- 局部变量表
- 操作数栈
- 动态连接
- 返回地址
- 大小设置 (面试点)
- -Xss JDK 1.8默认1M)
- 只入栈不出栈,会栈溢出(常见于循环调用、递归调用处理不当)
public class JavaStack {
/* public static class User{
public int id = 0;
public String name = "";
}*/
//常量
final String Fs ="常在河边走,哪有不湿鞋";
//静态变量
static String Ss ="以静制动";
//次数
int count =0 ;
//King老师出差
public void king(int money){
//13号技师
Object tech13 = new Object();
//调用一次13号服务
tech13.hashCode();
int i;
money = money -100; //花费100
count++;
//if(count ==2000) return;
king(money);
}
public static void main(String[] args)throws Throwable {
JavaStack javaStack = new JavaStack();
try {
javaStack.king(10000);
}catch (Throwable e){
//输出异常时循环的次数
System.out.println("栈异常!调用方法(king)的次数():"+javaStack.count);
throw e;
}
}
}

本地方法栈
- 定义
本地方法栈保存的是native方法的信息 - 当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单的动态链接并直接调用native方法(例如hashCode()方法)
- 虚拟机规范无强制规定,每个版本虚拟机自由实现
HotSpot直接把本地房发展和虚拟机栈合二为一
线程共享区域
方法区
- 类信息(class)
- 常量
- 静态变量
- 即时编译期编译后的代码
Java堆
- 对象实例(几乎所有的对象)
- 数组
堆的大小参数设置
- -Xmx 堆区内存可被分配的最大上限
- -Xms 堆区内存出事内存分配的大小
直接内存

不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域
- 如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作
- 这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常
深入辨析堆和栈
功能
- 以栈帧的方式存储方法调用的过程,并存储方法调用过程中基本数据类型的变量(int、short、long、byte、float、double、boolean、char)以及对象的引用变量(ref),其内存分配在栈上,变量出了作用域就会自动释放
- 而堆内存用来存储Java中的对象,无论是成员变量、局部变量,还是类变量,他们指向的对象都存储在堆内存中。
线程独享还是共享
- 栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即占内存可以理解成线程的私有内存。
- 堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
空间大小
- 栈的内存要远远小于堆内存,栈的深度是有限制的,可能发生StackOverFlowError的问题
虚拟机中对象

对象的内存布局

对象的访问定位

虚拟机优化技术--逃逸分析
逃逸分析
逃逸分析是目前JVM中比较前沿的优化技术,它不是直接优化手段而是为其他优化手段提供依据的分析技术
逃逸分析的基本行为就是分析对象动态作用域。
栈上分配
99% 对象都在堆上分配
1% 对象可能在创建时经逃逸分析,在栈上分配
如果是栈上分配,就不需要垃圾回收了,会随着线程的结束而消亡
public class StackAlloc {
public static class User{
public int id = 0;
public String name = "";
}
public static void alloc() {
User u = new User(); //Object 在堆上分配的() ,有逃逸分析的技术 ,在栈中分配的
u.id = 5;
u.name = "King";
}
public static void main(String[] args) {
long b = System.currentTimeMillis(); //开始时间
for(int i=0;i<100000000;i++) {//一个方法运行1亿次()
alloc();
}
long e = System.currentTimeMillis(); //结束时间
System.out.println(e-b);//打印运行时间:毫秒
}
}
牵扯到的JVM参数
-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)-XX:-DoEscapeAnalysis(关闭)
XX:DoEscapeAnalysis
-XX:+EliminateAllocations:标量替换(默认打开)
-XX:+UseTLAB 本地线程分配缓冲(默认打开)
-XX:+PrintGC(打印垃圾回收过程)
网友评论