深入理解JVM(二) JVM的运行时数据区

一、 JVM运行时数据区的组成

JDK < 1.8 时运行时数据区的组成

运行时数据区包括JVM堆、 JVM栈、方法区、本地方法栈和程序计数器。 下面具体说明各个区的作用。

1. 程序计数器

    每个线程启动的时候都会创建一个程序计数器,用来保存当前正在执行的JVM指令的地址,程序计数器归该线程私有

    程序计数器可以看做一个指向字节码指令的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖计数器来完成。

    由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令,因此,为了线程切换后能恢复到正常的执行位置,每条线程都需要一个独立的程序计数器,各线程之间计数器互不影响,独立存储。

    如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器的值则为空(Undefined)。此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

 

2.  虚拟机栈

    虚拟机栈也是线程私有的。当每个线程创建的时候,就会创建一个虚拟机栈,因此虚拟机栈也是有多个的,其生命周期与线程保持一致。  它用于存储当前线程运行方法时所需要的数据、指令、返回地址。

    在每个方法执行时,虚拟机栈都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至完成的过程,就对应着一个栈桢在虚拟机栈中由进栈到出栈的过程。

    思考: 一方法的调用产生一个栈桢,那么如果是递归调用同一个方法,那么该方法会产生一个栈桢还是多个栈桢呢?

    public void func(){

         func();   //递归调用

     }

    很明显,这样无限的递归很快将产生StackOverflowError。 如果递归调用一直只有一个栈桢的话,显然是不会造成栈溢出的。因此,递归调用的过程每一次调用方法都会产生一个栈桢。

 

    局部变量表存放了编译时可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用和返回地址。局部变量表的大小在编译期间就已经确定了。

    虚拟机栈会出现两种异常:

(1)如果线程请求的栈深度大于虚拟机所允许的深度(如过深的递归),将会抛出StackOverflowError异常;

(2)如果虚拟机栈动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
 

3. 本地方法栈

    本地方法栈与虚拟机栈的作用非常相似,区别就是:

    虚拟机栈为执行Java方法(也就是字节码)服务, 而本地方法栈为执行Native方法服务。

 

4. JVM堆

    java堆是被所有线程共享的一块内存区域,用来就是存放实例对象,几乎所有的对象实例都在这里分配内存。(本地栈中存放的是对象的引用

    JVM的对象存放区分为年轻代、老年代、永久代,我们将在下一节进行深入讲解。

   如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

 

5. 方法区

    方法区由各个线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据

    方法区中还包括运行时常量池,用于存放编译期生成的各种字面量、常量符号引用,这部分内容将在类加载后进入方法区的运行时常量中存放。当常量池无法再申请到内存时也会抛出OutOfMemoryError异常。

    从1.7版本开始,字符串常量从方法区移到了堆里面

思考:

以下信息分别放在哪个区呢?
 

​
String info = "hello world";   //字面值放方法区中常量池, 对象放堆

int age = 0;                   //变量放虚拟机栈

static float PI = 3.14;        //常量3.14放方法区中常量池, 静态变量放方法区中常量池

Object obj = new Factory();    //类放在方法区中, 实例放堆


​public void func(){           //方法放在方法区,常量放方法区常量池, 变量放虚拟机栈
    boolean sex = FEMALE;
}

int[] arr = new int[3];       //new产生了对象,放在堆中

二、 JDK1.7与1.8的区别

    方法区通常被称为“永久代”。JDK1.8之后元数据区取代了永久代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别是:元数据空间并不在虚拟机中,而是使用本地内存。

思考: 为什么要用元数据区代替永久代?这样做的好处是什么?

1. 官网给出的解释

  移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

2. 个人的观点

   永久代的最大值在启动时已经由参数-XX:MaxPermSize设置好。 而类、方法、字符串的大小只有在编译时才能确定。如果设置过小,容易导致OOM, 设置过大容易导致老年代溢出。而元数据区可以在默认情况下设置为动态扩容,方便根据实际需要灵活扩展。

    简化Full GC:每一个回收器有专门的元数据迭代器,可以在GC不进行暂停的情况下并发地释放类数据。

 

 

 

 

 

参考内容: 《深入理解Java虚拟机:JVM高级特性与最佳实践  周志明》。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值