文档章节

JVM的基本结构及其各部分详解2

就为这题
 就为这题
发布于 2017/02/28 09:47
字数 1651
阅读 2
收藏 0

3.2 栈帧组成之操作数栈

操作数栈是栈帧的主要内容之一,它主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。

操作数栈也是一个先进后出的数据结构,只支持入栈和出栈两种操作,许多java字节码指令都需要通过操作数栈进行参数传递。比如add指令,它就会在操作数栈中弹出两个整数并进行加法计算,计算结果会被入栈,如图:显示了iadd前后操作数栈的变化。

3.3 帧数据区

除了局部变量表和操作数栈,java栈帧还需要一些数据来支持常量池的解析、正常方法返回和异常处理等。大部分java字节码指令需要进行常量池访问,在帧数据区中保留着访问常量池的指针,方便程序访问常量池。

此外,当函数返回或者出现异常时,虚拟机必须恢复调用者函数的栈帧,并让调用者函数继续执行下去。对于异常处理,虚拟机必须有一个异常处理表,方便在发生异常时找到处理异常的代码,因此异常处理表也是帧数据区中重要的一部分,一个典型的异常处理表如下所示:

Exception table:

from   to    target   type

4        16      19      any

19       21      19      any

它表示在字节码偏移量4--16字节可能抛出任意异常,如果抛出异常,则跳转到字节码偏移量19处执行。当方法抛出异常时,虚拟机就会查找类似的异常表来处理,如果无法在异常表中找到合适的处理方法,则会结束当前函数调用,返回调用函数,并在调用函数中抛出相同的异常,并查找调用函数的异常表来进行处理。

3.4 栈上分配

栈上分配是java虚拟机提供的一项优化技术,它的基本思想是,对于那些线程私有的对象(这里指不可能被其他线程访问的对象),可以将他们打散分配到栈上,而不是分配到堆上。分配到栈上的好处是可以在函数调用结束后自行销毁,而不需要垃圾回收器的介入,从而提高系统的性能。

栈上分配的一个技术基础是进行逃逸分析,逃逸分析的目的是判断对象的作用域是否有可能逃逸出函数体。如下代码所示显示了一个逃逸对象:

private static User u;

public static void alloc(){

  u = new User();

  u.id = 5;

  u.name = "jim";

}

对象u是类的成员变量,该字段有可能被任何线程访问,因此属于逃逸对象,而以下对象显示了一个非逃逸对象:

public static void alloc(){

  User u = new User();

  u.id =  5;

  u.name = "jim";

}

在上述代码中,对象User u 以局部变量的形式存在,并且该对象并没有被alloc()函数返回或者出现任何形式的公开,因此它未发生逃逸,所以对于这种情况,虚拟机就有可能将User u 分配在栈上,而不是在堆上。

对于大量的零散小对象,栈上分配提供了一种良好的对象分配优化策略,栈上分配速度快,并且可以有效避免垃圾回收带来的负面影响。但由于栈和堆空间相比,栈空间较小,因此对于大对象无法也不适合在栈上分配。

实例1:测试非逃逸对象的分配空间位置

package com.jvm;

public class OnStackTest {
  public static class User{
    public int id = 0;
    public String name = "";
  }

  public static void alloc(){
    User u = new User();
    u.id = 5;
    u.name = "jim";
  }

  public static void main(String[] args) {
    long b = System.currentTimeMillis();
    for(int i=0;i<100000000;i++){
      alloc();
    }
    long e = System.currentTimeMillis();
    System.out.println(e-b);
  }
}

使用-Xmx10M -XX:+PrintGC 虚拟机参数运行代码:

[GC (Allocation Failure) 2048K->544K(9728K), 0.0015011 secs]
10

上述代码在主函数中进行了1亿次alloc()调用进行对象的创建,由于User对象实例需要占用约16byte的空间,因此累计分配空间将达到1.5G,如果堆空间小于这个值,就必然发生GC。而此时我们只分配了最大的堆内存为10M,如果这些对象在堆上创建,必然会引起大量的垃圾回收现象,查看垃圾回收日志,并没有。所以,说明其对象分配在栈上。

实例2:对比测试逃逸对象的分配空间位置:

package com.jvm;

public class OnStackTest {
  public static class User{
    public int id = 0;
    public String name = "";
  }
  public static User u;
  public static void alloc(){
    u = new User();
    u.id = 5;
    u.name = "jim";
  }

  public static void main(String[] args) {
    long b = System.currentTimeMillis();
    for(int i=0;i<100000000;i++){
      alloc();
    }
    long e = System.currentTimeMillis();
    System.out.println(e-b);
  }
}

同样使用虚拟机参数-Xmx10M -XX:+PrintGC设置最大堆空间和打印垃圾回收日志,运行此代码:

可见,发生大量的垃圾回收现象,说明此时堆内存远远不够,需要不断的进行垃圾回收。

 

4 方法区

和堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,比如类的字段、方法、常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区的溢出,虚拟机同样会抛出内存溢出错误。

在JDK1.6、JDK1.7中,方法区可以理解为永久区(Perm)。永久区可以使用参数-XX:PermSize和-XX:MaxPermSize指定,默认情况下,-XX:MaxPermSize为64M。一个大的永久区可以保存更多的类信息。如果系统使用了一些动态代理,那么有可能会在运行时生成大量的类,如果这样,就需要设置一个合理的永久区大小,确保不发生永久区内存溢出。

 

在JDK1.8中,永久区已经被彻底移除,取而代之的是元数据区,元数据区大小可以使用参数-XX:MaxMetaspaceSize指定(一个大的元数据区可以使系统支持更多的类),这是一块堆外的直接内存。与永久区不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。

如果元数据区发生异常,虚拟机一样会抛出异常。

© 著作权归作者所有

共有 人打赏支持
就为这题
粉丝 9
博文 33
码字总数 37965
作品 0
海淀
程序员
JVM执行子系统探究——类文件结构初窥

类文件(.class)是搞java的都非常熟悉的文件,一般我们在编写java之后文件之后,首先通过javac工具生成.class类字节码文件,而后在执行程序的时候由虚拟机加载执行。那么为什么要生成.clas...

oO脾气不坏Oo
2014/04/27
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
【Java并发专题】27篇文章详细总结Java并发基础知识

努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! github:https://github.com/CL0610/Java-concurrency,欢迎题issue和Pull request。所有的文档都是自己亲自码的,如果觉...

你听___
05/06
0
0
图说 Java:理解 Java 机制最受欢迎的 8 幅图

世间总是一图胜过千万言,下面的8幅图来自于 Program Creek 的 Java教程 ,目前这是该网站最受欢迎的文章. 希望本文能帮你回顾你已经知道的那些知识。如果图片讲解的不够清晰,你可能需要阅读详...

大数据之路
2013/10/30
0
3
大数据开发培训:0基础学习Java编程语言有哪些知识点?

Java 技术通用、高效、具有平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网等,学习Java首先要知道学习知识点有哪些。在这就用加米谷大数据培训...

加米谷大数据
07/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

python中cv2模块imread函数

导入 >> import cv2>> import numpy as np 读图片 >> image_arr = cv2.imread('file_path') 灰度图扩展成彩色图 可以通过图片的channel判断是否是灰度图。如果需要可以将灰度图扩展到RGB......

温子寒
刚刚
0
0
利用剪切板JS API优化输入框的粘贴体验

直接复制记录下 /**@description 表单输入框粘贴体验优化,出处https://www.zhangxinxu.com/wordpress/?p=8003@author zhangxinxu*/// 遍历所有的输入框[].slice.call(document.qu...

红羊在厦门
7分钟前
0
0
awk复习

10月22日任务 复习 awk调用外部变量 -v参数设置内部变量调用外部变量 [root@castiel-Lu awk]# a=44[root@castiel-Lu awk]# echo "ABCD" | awk -v GET_A=$a '{print GET_A}'44 更复杂的...

robertt15
13分钟前
0
0
2018CTF大赛学习

1.C伪随机数,可预测,可用来做加密解密,如 int main() { // This program will create same sequence of // random numbers on every program run for(int i = 0; ......

simpower
21分钟前
0
0
XamarinEssentials教程移除键值首选项的键值

XamarinEssentials教程移除键值首选项的键值 如果开发者不再使用首选项中的某一项时,可以将该项移除掉。此时可以使用Preferences类的Remove()方法,该方法可以将存在于首选项中的指定键以及...

大学霸
29分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部