java运行时数据区域和类结构详解

Java运行时数据区域

java运行时数据区可以分为:方法区、虚拟机栈、本地方法栈、堆和程序计数器

线程私有:虚拟机栈、本地方法栈、程序计数器

线程共享:方法区、堆

程序计数器

一块较小的内存空间,当前线程所执行字节码的行号指示器,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

每条线程都拥有一个独立的程序计数器。

Java虚拟机栈

线程私有的,它的生命周期与线程相同。

每个方法被执行时,java虚拟机都会创建一个栈帧,用于存储 局部变量表、操作数栈、动态链接、方法出口等信息

动态链接:符号在运行中转化为直接引用的过程,就是动态连接(预支对应的静态连接,是指类加载阶段将静态的符号引用转成)。

本地方法栈

作用于java虚拟机栈类似,不过作用的是本地的native 方法。

java堆

线程共享的一块内存区域,用来存放对象实例。“几乎”所有的对象都分配在堆中。

由于及时编译,特别是逃逸分析技术日益请打,对象也不一定分配在堆中(可能栈上分配标量替换)。

java堆中可以划分出多个线程私有的分配缓冲区(TLAB)来提高对象分配效率,这个TLAB只保证该线程才能在此分配,但是所有线程都是可以进行访问的。

方法区

线程共享,存放虚拟机加载的类型信息常量静态变量即时编辑器编译后的代码缓存等数据。

方法区的运行时常量池:存放 类加载器中加载Class文件中的常量池表。

java对象内存分配

字节码new 指令 -> 检查常量池 ->类加载器(加载、连接(检查、准备、解析)、初始化)

检查后,就要为新生对象进行内存分配了。分配策略:

逃逸分析

分析对象的作用域是否在本方法中,如果只有在本方法中,那么他可以栈上分配,逃逸分析jdk7以后是默认开启的。

new 的对象不一定在堆中,他可能在栈上分配和标量替换

栈上分配:JVM调优方式之一,方法的对象如果不逃逸在外,那么它可以分配在栈上,他的生命周期与方法调用一致,减小GC的压力。

标量替换:如果对象不存在逃逸,JVM可能不会创建该对象,而是将该对象变量分解成若干个成员变量所替换,这样就可以在栈帧或寄存器上分配(不用连续的空间),jdk7默认开启。标量替换优先于栈上分配。

TLAB:线程本地分配缓存区(也是堆中)

Eden中分配内存时,如果多个线程都同时分配内存,会造成指针碰撞情况,为了提高对象分配效率,使用TLAB。

线程初始化时,会申请一点指定大小的内存,只提供当前线程进行内存分配,这样每个线程都单独拥有一个空间。

TLAB是虚拟机在堆内存的eden划分出来的一块专用空间。

TLAB没有没有足够空间来满足操作时,需要向当前线程重新申请新的TLAB

java类文件结构

class 字节码的文件结构,严格按照顺序记性解析

类型 名称 备注
u4 magic 魔数,识别Class文件格式,值为:0XCAFEBABE
u2 minor_version 副版本号
u2 major_version 主版本号,45-?,JDK13为57,JDK8为52
u2 constant_pool_count 常量池计算器
cp_info constant_pool 常量池,class资源库
u2 access_flags 访问标志,public、final等9个标志。有16个标志位,每一位标识一种访问标志。
u2 this_flags 类索引,常量池中的索引值
u2 super_class 父类索引,常量池中的索引值
u2 interfaces_count 接口计数器
u2 interfaces 接口索引集合,常量池中的索引值
u2 fields_count 字段个数
field_info fields 字段集合, 字段标志(public、static等)、字段名常量索引、描述常量索引(类型)
u2 methods_count 方法计数器
method_info methods 方法集合,和字段集合差不多,方法标志、方法名索引、方法描述索引(返回类型、方法参数列表)
u2 attributes_count 附加属性计数器
attribute_info attributes 附加属性集合

常量池

常量池分为:字面量符号引用

字面量:文本字符串、final常量值等

符号引用:

  • 类、接口全限定名
  • 字段、方法的名称和描述符
  • 方法句柄和类型
  • 动态调用点和动态常量

常量池项目类型:

属性表

Class 文件、字段表、方法表都可以携带自己的属性表集合,描述某些场景专有的信息

属性(部分)有:

比如Code属性,

类加载机制

类加载过程:

加载 -> 链接 (验证、准备、解析) -> 初始化

加载:用类加载器加载字节码

验证:验证字节码的合法性(满足约束条件)

准备:被加载类的静态字段分配内存

解析:符号引用解析成实际引用。

初始化:初始化常量、静态类

类加载器:

启动类加载器:加载最基础的最重要的类,如JRE的lib下的jar包中的类

扩展类加载器:他的弗雷是启动类加载器,主要加载相对次要但又通用的类,如JRE的lib/ext下的jar的类

应用类加载器:他的父类是扩展类加载器,负责加载应用程序路径下的类。(指虚拟机参数 -cp/-classpath、系统变量 java.class.path或环境变量 CLASSPATH 所指定的路径)

同一字节流经过不同类加载器加载,也会得到两个不同的类。

双亲委派模式:让父加载器尽量加载

双亲委派模式的破坏:

1)如果上层类加载器加载的类 加载 下层的类加载器加载的类

java引入了上下文类加载器,可以打通弗雷加载器去请求子类加载器加载的行为。如JNDI调用服务代码的时候。

2)OSGI热部署,使用网状的类加载模式。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java运行时数据区域(内存划分)的深入讲解

    1. 程序计数器(线程私有) 程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器(切换线程后,能恢复到正确的执行位置). 2. Java虚拟机栈(线程私有) (1) 概念   虚拟机栈描述的是Java方法执行的内存模型 : 每个方法执行的同时都会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口 等信息.每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程.声明周期与线程相同. 关于栈帧的理解: 创建启动一个线程,就创建了一个虚拟机

  • Java运行时数据区划分原理解析

    Java中对象创建,内存分配,垃圾回收的权力交给了虚拟机,这其中有利也有弊,程序员也减轻了负担,但是如果不熟悉Java的内存区域划分,一旦出现内存溢出和泄漏,将会很难定位问题的根源,这就有必要了解Java的运行时数据区划分. 方法区(Method Area) 是由各个线程共享的内存区域,用来存储已被虚拟机加载的类型信息.常量.静态变量.即时编译器编译后的代码缓存等数据. 堆(Heap) Java虚拟机所管理的一块最大的内存区域,由所有的线程共享的一块内存区域:堆内存在虚拟机启动时创建,用来存放对

  • Java虚拟机运行时数据区域汇总

    程序计数器(Program Counter) 程序计数器作为一个概念模型,这个是用来指示下一条需要执行的字节码指令在哪. Java的多线程实际上是通过线程轮转做到的,如果是一个单核的机器(或单cpu),严格意义上在一个时间块中只会有一个线程在执行.为了线程切换以后能恢复到正确的执行位置,每个线程都需要有一个单独的计数器,每个计数器之间要是独立的互不干扰. 如果线程执行的是Java方法,那么PC指向的是正在执行的虚拟机字节码指令的区域,如果执行的是native方法,那么它是undefined. J

  • Java内存划分:运行时数据区域

    目录 1. 程序计数器(线程私有) 2. Java虚拟机栈(线程私有) (1) 概念 (2) 下面我们来分析一段代码 3. 本地方法栈(线程私有) 4. Java堆(线程共享) 5. 方法区(线程共享) 6. 运行时常量池(方法区的一部分) (1) 概念 (2) 补充: 其他常量池 总结 1. 程序计数器(线程私有) 程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器(切换线程后,能恢复到正确的执行位置). 2. Java虚拟机栈(线程私有) (1) 概念 虚拟机栈描

  • java运行时数据区域和类结构详解

    Java运行时数据区域 java运行时数据区可以分为:方法区.虚拟机栈.本地方法栈.堆和程序计数器 线程私有:虚拟机栈.本地方法栈.程序计数器 线程共享:方法区.堆 程序计数器 一块较小的内存空间,当前线程所执行字节码的行号指示器,它是程序控制流的指示器,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 每条线程都拥有一个独立的程序计数器. Java虚拟机栈 线程私有的,它的生命周期与线程相同. 每个方法被执行时,java虚拟机都会创建一个栈帧,用于存储 局部变量表.操

  • Java内存模型与JVM运行时数据区的区别详解

    首先,这两者是完全不同的概念,绝对不能混为一谈. 1.什么是Java内存模型? Java内存模型是Java语言在多线程并发情况下对于共享变量读写(实际是共享变量对应的内存操作)的规范,主要是为了解决多线程可见性.原子性的问题,解决共享变量的多线程操作冲突问题. 多线程编程的普遍问题是: 所见非所得 无法肉眼检测程序的准确性 不同的运行平台表现不同 错误很难复现 故JVM规范规定了Java虚拟机对多线程内存操作的一些规则,主要集中体现在volatile和synchronized这两个关键字. vo

  • JVM运行时数据区划分原理详解

    Java内存空间 内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了JAVA在运行过程中内存申请.分配.管理的策略,保证了JVM的高效稳定运行.不同的jvm对于内存的划分方式和管理机制存在着部分差异(对于Hotspot主要指方法区) (图源阿里)JDK8的元数据区+JIT编译产物 就是JDK8以前的方法区 JavaAPI中的Runtime public class Runtime extends Object Every Java

  • 详解Java虚拟机管理的内存运行时数据区域

    详解Java虚拟机管理的内存运行时数据区域 概述 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁. 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基

  • Java运行时数据区概述详解

    Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,如图所示: 程序计数器 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器. 在虚拟机的概念模型中(仅是概念模型,各种虚拟机可能会通过一些更加高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 如果线程正在执行一个Java方法,则这个计数

  • Java异常处理运行时异常(RuntimeException)详解及实例

      Java异常处理运行时异常(RuntimeException)详解及实例 RuntimeException RunntimeException的子类: ClassCastException 多态中,可以使用Instanceof 判断,进行规避 ArithmeticException 进行if判断,如果除数为0,进行return NullPointerException 进行if判断,是否为null ArrayIndexOutOfBoundsException 使用数组length属性,避免越

  • java与JSON数据的转换实例详解

    java与JSON数据的转换实例详解 JSON与JAVA数据的转换(JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互.) 代码中有这么一句,是后台的封装数据. JSONObject jo = JSONObject.fromObject(map); 常见的java代码转换成json --请注意,这个方法曾经给我造成过困惑.因为,它在对Object转换的时候是按照domain类中的所有getXXX()方

  • 深入理解Java运行时数据区_动力节点Java学院整理

    JVM体系结构和运行时数据区概述 要理解JVM的运行时数据区, 必须先要理解JVM的体系结构, 因为虚拟机的体系结构基本上解释了"为什么会有这些运行时数据区" . JVM的体系结构如下: 由此可见, 运行时数据区的划分, 是和JVM的体系结构相关的. 本文主要介绍运行时数据区的划分, 对体系结构不做深入的讲解. 简单概括一下, 类加载器子系统用于将class文件加载到虚拟机的运行时数据区中(准确的说应该是方法区) . 可以认为执行引擎是字节码的执行机制, 一个线程可以看做是一个执行引擎

随机推荐