java栈帧还是需要一些数据支持常量池的解析、正常方法的返回和异常的处理。大部分的java字节码指令需要进行常量池的访问,在栈帧数据区中保存着访问常量池的指针,方便程序访问java常量池。如下图所示:

当函数返回或者程序出现异常的时候,jvm虚拟机必须恢复调用者函数的栈帧,并且让调用者函数继续执行。什么意思呢?举一个通俗的例子:

a()调用b()当b()返回的时候肯定继续让a()继续执行对吧。b()抛出异常的时候a()肯定也需要处理对吧。

对于异常的处理,jvm是如何处理的呢?虚拟机肯定必须有一个异常处理表,方便发生异常的时候找到处理异常的代码,方便程序继续运转,因此异常处理表Exception table也是帧数据区中很重要的一部分。怎么获取Exception table
中的信息呢操作如下:

执行命令

javap -verbose命令输出它的字节码(不懂这个命令的可以javap -help查看)

from   to  target type

0     8     19   any

表示在字节码
0-8个字节可能抛出异常,如果遇到异常,则跳转到字节码偏移量19出执行,当方法抛出异常的时候,虚拟机就会查找类似的异常表进行处理,如果无法找到异常表中对应的信息,则会结束当前程序的调用,返回调用函数,并在调用函数中抛出这个异常信息,并查找函数的异常表进行处理。

通俗易懂的说:

a()调用b()  当b()方法中有异常的时候,b()中处理了则继续执行,程序没有处理也就没有异常表信息直接抛异常到a()方法,b()栈移除,a()处理了继续流转,没有处理直接异常抛出。

1.1.1. 栈上分配

栈上分配是java虚拟机提供的一种优化技术,基本思想是,对于那些线程私有对象(不可能被其他线程访问的对象),可以将他们打散分配到栈上,而不是分配在堆上,(是不是很毁三观啊,是不是以前认识的都不对啊白着急)。分配在栈上面的好处函数调用完成后直接自杀销毁,而不需要垃圾回收机制介入回收,所以这样对于性能的提升还是蛮有帮助的。

栈上分配有一个前提条件:开启逃逸分析(必须否则不会栈上分配),逃逸分析的目的就是判断对象的作用域是否有可能逃逸出函数体。如下代码:

private static Useruser;

public static void alloc(){

user=new User();

user.setId(5);

user.setName("springok");

}

对象User user 因为是static  方法中因此可以被任何线程访问到。所以属于逃逸对象。

下面的代码显示了一个非逃逸对象:

public static void alloc(){

User user;

user=new User();

user.setId(5);

user.setName("springok");

}

因为不可以被其他的线程访问到。对象user以局部变量形式存在,该对象没有被返回出去,也没有任何地方可以访问到在其他的方法中,因此是非逃逸的,所以虚拟机可能将user分配在栈上。下面的代码对其结论进行证明:

public static void alloc() {

User
user = new User();

user.setId(5);

user.setName("springok");

}

public static void main(String[]args) {

long begin = System.currentTimeMillis();

for (int i = 0;i < 100000000;i++)
{

alloc();

}

long end = System.currentTimeMillis();

System.out.println(end-begin);

}

上面代码进行一亿次调用进行对象的创建,累计分配的内存可能达到了1.5G,如果堆空间小于这个1.5G肯定会GC垃圾回收,所以我们开启垃圾回收的参数。

运行配置的参数如下:

-server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

jvm参数使用说明:

-server  因为在server模式才能启用逃逸分析。

-XX:+DoEscapeAnalysis
启用逃逸分析

-Xmx10m 堆空间最大10MB.如果对象在堆上创建肯定会GC垃圾回收的。

-XX:+PrintGC 打印GC日志。

-XX:+EliminateAllocations
开启标量替换默认就是开启的,允许对象打散分配到栈上。

user对象拥有的id和name属性将会被视为独立的局部变量分配。

-XX:-UseTLAB 关闭TLAB

程序执行后完整的打印如下:

5

可以看到没有任何形式的GC输出,程序执行完毕了。说明user对象确实在分配过程被优化了。

如果关闭逃逸分析或者标量替换任意一个,再次执行程序,就会看到大量的GC日志如下:

[GC 3226K->474K(9920K), 0.0001595 secs]

[GC 3226K->474K(9920K), 0.0001488 secs]

[GC 3226K->474K(9920K), 0.0001726 secs]

[GC 3226K->474K(9920K), 0.0001614 secs]

1556

代码侧面印证了结论的正确性。

第一说明了上面的结论,栈上分配依赖逃逸分析和标量替换的实现,第二确实开启了对象被分配在栈上,而不是堆上。因为在堆上肯定会GC垃圾回收的。

对于大量零散的小对象,栈上分配提供了很好的优化策略,栈上分配速度快,可以避免垃圾回收。但由于和堆相比栈的空间小,因此大对象不适合在栈上分配。

java虚拟机 jvm 栈数据区的更多相关文章

  1. Java虚拟机运行时数据区

    运行时数据区程序计数器Java虚拟机栈本地方法栈Java堆(GC堆)方法区运行时常量池 运行时数据区 Java虚拟机在运行Java程序时,会将它所管理的内存划分为若干个内存区域.这些数据区域有各自的用 ...

  2. 面试常问的 Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

  3. Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

  4. 《深入理解Java虚拟机》(二)Java虚拟机运行时数据区

    Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第二章 ,为了整理思路,简单记录一下,方便后期查阅. 2.2 运行时数据区域 Java虚拟机 ...

  5. 【深入理解Java虚拟机】Java虚拟机运行时数据区

    Java虚拟机运行时数据区 线程私有 程序计数器 1.当前线程所执行的字节码的行号指示器. 2.唯一不会发生OutOfMemoryError的区域 3.如果执行的是java方法,计数器值为虚拟机字节码 ...

  6. 【JVM从小白学成大佬】2.Java虚拟机运行时数据区

    目录 1.运行时数据区介绍 2.堆(Heap) 是否可能有两个对象共用一段内存的事故? 3.方法区(Method Area) 4.程序计数器(Program Counter Register) 5.虚 ...

  7. 【JVM学习】2.Java虚拟机运行时数据区

    来源: 公众号: 猿人谷 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题.但是,往往都会令我比较尴尬,我还话音未落,面试者就会"背诵& ...

  8. Java 虚拟机运行时数据区详解

    本文摘自深入理解 Java 虚拟机第三版 概述 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...

  9. 笔记:Java虚拟机运行时数据区

    Java虚拟机在执行Java程序的过程中会把它管的内存划分为以下若干个不同的区域: 1.程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器:由于Java虚拟机的 ...

随机推荐

  1. [POJ 3487]The Stable Marriage Problem

    Description The stable marriage problem consists of matching members of two different sets according ...

  2. [Luogu 3807]【模板】卢卡斯定理

    Description 给定n,m,p(1≤n,m,p≤10​^5​​) 求 C_{n+m}^{m} \mod p 保证P为prime C表示组合数. 一个测试点内包含多组数据. Input 第一行一 ...

  3. 多项式的基本运算(FFT和NTT)总结

    设参与运算的多项式最高次数是n,那么多项式的加法,减法显然可以在O(n)时间内计算. 所以我们关心的是两个多项式的乘积.朴素的方法需要O(n^2)时间,并不够优秀. 考虑优化. 多项式乘积 方案一:分 ...

  4. C++Primer学习——函数

    编译器能以任意顺序对形参进行求值 函数的返回类型不能是数组类型和函数类型. 函数开始时为形参分配内存,一旦函数结束,形参也就被销毁了. 如果弄成静态局部变量,那么回到程序终止结束时才被销毁. void ...

  5. python,for,while循环控制

    1.for循环 for循环 for i in range(0,5): for j in range(0,5): print('#'*5) 2.while 循环 import random #get n ...

  6. audioplayer.js插件的使用及小bug

    之前在项目里用audioplayer.js做的一个页面,改了布局样式,还有插件自身有个bug就是audio添加autoplay属性后有两个音频播放,其中一个无法控制,会一直播放,我查看了官网的demo ...

  7. js页面刷新跳转的几种方式及区别

    跳转常用方法: window.location.href="index.php"; window.history.back(-1);//类似于按钮,参数是负几,就后退几次. win ...

  8. C指针 指针和数组 二维数组的指针 指针应用

    直接到变量名标识的存储单元中读取变量的值--直接寻址 通过其他变量间接找到变量的地址读取变量的值--间接寻址 指针变量存放地址 显示变量的地址 指针变量使用前必须初始化,若不知指向哪,可先指向NULL ...

  9. JavaScript 题目(作用域)

    var length = 10 function fn(){ alert(this.length) } var obj = { length: 5, method: function(fn) { fn ...

  10. gdb调试的基本使用

    GDB调试 启动程序准备调试 GDB yourpram 或者 先输入GDB 然后输入 file yourpram 然后使用run或者r命令开始程序的执行,也可以使用 run parameter将参数传 ...