你的位置:亚洲日韩欧美人成黄瓜_好嗨呦直播app下载_大胆西西人体gogo_美女下面直流白水视频_人妻中出无码一区二区_最新婬乱小说午夜视频_无码艳妇乳肉豪妇荡乳 > 女同学被下药强啪到爽 >


耸动的粉嫩小屁股尿道那么腹地栈即是一个 C 的栈

发布日期:2022-09-23 05:53    点击次数:53


レズエステ人妻高級耸动的粉嫩小屁股尿道

 

这篇著述说明了Java 诬捏机(JVM)的里面架构。下图清楚了盲从 Java SE 7 表率的典型的 JVM 中枢里面组件。

 

JVM内幕:Java诬捏机详解

 

上图清楚的组件分两个章节说明。第一章参谋针对每个线程创建的组件,第二章节参谋了线程无关组件。

线程 JVM 系统线程 每个线程关联的 步地计数器 栈 腹地栈 栈限制 栈帧 局部变量数组 操作数栈 动态链接 线程分享 堆 内存搞定 非堆内存 即时编译 步骤区 类文献结构 类加载器 更快的类加载 步骤区在何处 类加载器参考 运行时常量池 终点表 标记表 Interned 字符串

线程

这里所说的线程指步地试验经由中的一个线程实体。JVM 允许一个愚弄并发试验多个线程。Hotspot JVM 中的 Java 线程与原生操作系统线程有径直的映射干系。当线程腹地存储、缓冲折柳拨、同步对象、栈、步地计数器等准备好以后,就会创建一个操作系统原生线程。Java 线程收尾,原生线程随之被回收。操作系统真贵调理通盘线程,并把它们分拨到任何可用的 CPU 上。当原生线程开动化收场,就会调用 Java 线程的 run() 步骤。run() 复返时,被处理未拿获终点,原生线程将证明由于它的收尾是否要停止 JVM 进度(比如这个线程是临了一个非防守线程)。当线程收尾时,会开释原生线程和 Java 线程的通盘资源。

JVM 系统线程

要是使用 jconsole 大略其它调试器,你会看到好多线程在后台运行。这些后台线程与触发 public static void main(String[]) 函数的干线程以及干线程创建的其他线程一齐运行。Hotspot JVM 后台运行的系统线程主要有底下几个:

 

JVM内幕:Java诬捏机详解

 

线程关联组件

每个运行的线程都包含底下这些组件:

步地计数器(PC)

PC 指面前辅导(或操作码)的地址,腹地辅导除外。要是面前景序是 native 步骤,那么PC 的值为 undefined。通盘的 CPU 都有一个 PC,典型情景下,每试验一条辅导 PC 都会自增,因此 PC 存储了指向下一条要被试验的辅导地址。JVM 用 PC 来追踪辅导试验的位置,PC 将内容上是指向步骤区(Method Area)的一个内存地址。

栈(Stack)

每个线程领有我方的栈,栈包含每个步骤试验的栈帧。栈是一个后进先出(LIFO)的数据结构,因此面前试验的步骤在栈的顶部。每次步骤调用时,一个新的栈帧创建并压栈到栈顶。当步骤渊博复返或抛出未拿获的终点时,栈帧就会出栈。除了栈帧的压栈和出栈,栈弗成被径直操作。是以不错在堆上分拨栈帧,何况不需要一语气内存。

Native栈

并非通盘的 JVM 终了都因循腹地(native)步骤,那些提供因循的 JVM 一般都会为每个线程创建腹地步骤栈。要是 JVM 用 C-linkage 模子终了 JNI(Java Native Invocation),那么腹地栈即是一个 C 的栈。在这种情况下,腹地步骤栈的参数轨则、复返值和典型的 C 步地交流。腹地步骤一般来说不错(依赖 JVM 的终了)反过来调用 JVM 中的 Java 步骤。这种 native 步骤调用 Java 会发生在栈(一般是 Java 栈)上;线程将离开腹地步骤栈,并在 Java 栈上开采一个新的栈帧。

栈的限制

栈不错是动态分拨也不错固定大小。要是线程肯求一个突出允许界限的空间,就会抛出一个StackOverflowError。要是线程需要一个新的栈帧,关联词莫得裕如的内存不错分拨,就会抛出一个 OutOfMemoryError。

栈帧(Frame)

每次步骤调用都会新建一个新的栈帧并把它压栈到栈顶。当步骤渊博复返大略调用经由中抛出未拿获的终点时,栈帧将出栈。更多对于终点处理的细节,不错参考底下的终点信息表章节。

每个栈帧包含:

局部变量数组 复返值 操作数栈 类面前景序的运行时常量池援用

局部变量数组

局部变量数组包含了步骤试验经由中的通盘变量,包括 this 援用、通盘步骤参数、其他局部变量。对于类步骤(也即是静态步骤),步骤参数从下标 0 脱手,对于对象步骤,位置0保留为 this。

有底下这些局部变量:

boolean byte char long short int float double reference returnAddress

除了 long 和 double 类型之外,通盘的变量类型都占用局部变量数组的一个位置。long 和 double 需要占用局部变量数组两个一语气的位置,因为它们是 64 位双精度,其它类型都是 32 位单精度。

操作数栈

操作数栈在试验字节码辅导经由中被用到,这种款式访佛于原生 CPU 寄存器。大部分 JVM 字节码把工夫破耗在操作数栈的操作上:入栈、出栈、复制、交换、产生消费变量的操作。因此,局部变量数组和操作数栈之间的交换变量辅导操作通过字节码频繁试验。比如,一个简短的变量开动化语句将产生两条跟操作数栈交互的字节码。

int i; 

被编译成底下的字节码:

0: iconst_0 // Push 0 to top of the operand stack 1: istore_1 // Pop value from top of operand stack and store as local variable 1 

更多对于局部变量数组、操作数栈和运行时常量池之间交互的详备信息,不错在类文献结构部分找到。

动态链接

每个栈帧都有一个运行时常量池的援用。这个援用指向栈帧面前运行步骤场地类的常量池。通过这个援用因循动态链接(dynamic linking)。

C/C++ 代码一般被编译成对象文献,然后多个对象文献被链接到一齐产生可试验文献大略 dll。在链接阶段,每个对象文献的标记援用被替换成了最终试验文献的相对偏移内存地址。在 Java中,链接阶段是运行时动态完成的。

当 Java 类文献编译时,通盘变量和步骤的援用都被当做标记援用存储在这个类的常量池中。标记援用是一个逻辑援用,内容上并不指向物理内存地址。JVM 不错选拔标记援用领略的时机,一种是当类文献加载并校验通事后,女性这种领略款式被称为饥饿款式。另外一种是标记援用在第一次使用的时候被领略,这种领略款式称为惰性款式。不管怎么 ,JVM 必须要在第一次使用标记援用时完成领略并抛出可能发生的领略诞妄。绑定是将对象域、步骤、类的标记援用替换为径直援用的经由。绑定只会发生一次。一朝绑定,标记援用会被整个替换。要是一个类的标记援用还莫得被领略,那么就会载入这个类。每个径直援用都被存储为相对于存储结构(与运行时变量或步骤的位置关联联的)偏移量。

线程间分享

堆被用来在运行时辰拨类实例、数组。弗成在栈上存储数组和对象。因为栈帧被考虑为创建以后无法调治大小。栈帧只存储指向堆中对象或数组的援用。与局部变量数组(每个栈帧中的)中的原始类型和援用类型不同,对象老是存储在堆上以便在步骤收尾时不会被移除。对象只可由垃圾回收器移除。

为了因循垃圾回收机制,堆被分为了底下三个区域:

腾达代 鄙俚被分为 Eden 和 Survivor 老年代 永恒代

内存搞定

对象和数组永恒不会显式回收,而是由垃圾回收器自动回收。鄙俚,经由是这么的:

新的对象和数组被创建并放入老年代。 Minor垃圾回收将发生在腾达代。依旧存活的对象将从 eden 区移到 survivor 区。 Major垃圾回收一般会导致愚弄进度暂停,它将在三个区内迁移对象。仍然存活的对象将被从头生代迁移到老年代。 每次进行老年代回收时也会进行永恒代回收。它们之中任何一个变满时,都会进行回收。

非堆内存

非堆内存指的是那些逻辑上属于 JVM 一部分对象,但内容上不在堆上创建。

非堆内存包括: 永恒代,包括: 步骤区 驻留字符串(interned strings)

代码缓存(Code Cache):用于编译和存储那些被 JIT 编译器编译成原生代码的步骤。

即时编译(JIT)

Java 字节码是说明试验的,关联词莫得径直在 JVM 宿主试验原生代码快。为了栽培性能,Oracle Hotspot 诬捏契机找到试验最频繁的字节码片断并把它们编译成原期望器码。编译出的原期望器码被存储在非堆内存的代码缓存中。通过这种步骤,Hotspot 诬捏机将量度底下两种工夫蹧跶:将字节码编译资腹地代码需要的稀罕工夫和说明试验字节码蹧跶更多的工夫。

步骤区

步骤区存储了每个类的信息,比如:

Classloader 援用 运行时常量池 数值型常量 字段援用 步骤援用 属性 字段数据 字段名 类型 修饰符 属性(Attribute) 针对每个字段的信息 步骤数据 步骤名 复返值类型 参数类型(按轨则) 修饰符 属性 每个步骤 步骤代码 字节码 操作数栈大小 局部变量大小 局部变量表 终点表 每个终点处理器 脱手点 收尾点 终点处理代码的步地计数器(PC)偏移量 被拿获的终点类对应的常量池下标 每个步骤

通盘线程分享团结个步骤区,因此拜访步骤区数据的和动态链接的进度必须线程安全。要是两个线程试图拜访一个还未加载的类的字段或步骤,必须只加载一次,而且两个线程必须等它加载收场才能不时试验。

类文献结构

一个编译后的类文献包含底下的结构:

 

JVM内幕:Java诬捏机详解

 

 

JVM内幕:Java诬捏机详解

 

不错用 javap 搜检编译后的 java class 文献字节码。

要是你编译底下这个简短的类:

package org.jvminternals; public class SimpleClass {  public void sayHello() {  System.out.println("Hello");  } } 

运行底下的敕令,就不错获取底下的闭幕输出: javap -v -p -s -sysinfo -constants classes/org/jvminternals/SimpleClass.class。

 

JVM内幕:Java诬捏机详解

 

 

JVM内幕:Java诬捏机详解

 

这个 class 文献展示了三个主要部分:常量池、构造器步骤和 sayHello 步骤。

常量池:提供了鄙俚由标记表提供的交流信息,详备刻画见下文。 步骤:每一个步骤包含四个区域, 签名和拜访标签 字节码 LineNumberTable:为调试器提供源码中的每一溜对应的字节码信息。上头的例子中,亚洲精品无码专区剧情Java 源码里的第 6 行与 sayHello 函数字节码序号 0 关联,第 7 行与字节码序号 8 关联。 LocalVariableTable:列出了通盘栈帧中的局部变量。上头两个例子中,独一的局部变量即是 this。

这个 class 文献用到底下这些字节码操作符:

 

JVM内幕:Java诬捏机详解

 

跟任何典型的字节码同样,操作数与局部变量、操作数栈、运行时常量池的主要交互如下所示。

构造器函数包含两个辅导。领先,this 变量被压栈到操作数栈,然后父类的构造器函数被调用,而这个构造器会消费 this,之后 this 被弹出操作数栈。

 

JVM内幕:Java诬捏机详解

 

sayHello() 步骤愈加复杂,正如之前说明的那样,因为它需要用运行时常量池中的指向标记援用的委果援用。第一个操作码 getstatic 从System类中将out静态变量压到操作数栈。下一个操作码 ldc 把字符串 “Hello” 压栈到操作数栈。临了 invokevirtual 操作符会调用 System.out 变量的 println 步骤,从操作数栈作弹出”Hello” 变量行为 println 的一个参数,并在面前哨程开采一个新栈帧。

 

JVM内幕:Java诬捏机详解

 

类加载器

JVM 启动时会用 bootstrap 类加载器加载一个开动化类,然后这个类会在public static void main(String[])调用之前完成链接和开动化。试验这个步骤会试验加载、链接、开动化需要的稀罕类和接口。

加载(Loading)是这么一个经由,找到代表这个类的 class 文献或把柄特定的名字找到接口类型,然后读取到一个字节数组中。接着,这些字节会被领略老师它们是否代表一个 Class 对象并包含正确的 major、minor 版块信息。径直父类的类和接口也会被加载进来。这些操作一朝完成,类大略接口对象就从二进制示意中创建出来了。

链接(Linking)是校验类或接口并准备类型和父类父接口的经由。链接经由包含三步:校验(verifying)、准备(preparing)、部分领略(optionally resolving)。

校验会证明类大略接口示意是否结构正确,以及是否罢免 Java 讲话和 JVM 的语义条件,比如会进行底下的查验:

步地一致且步地化正确的标记表 final 步骤和类莫得被重载 步骤罢免拜访限度重要词 步骤参数的数目、类型正确 字节码莫得不妥的操作栈数据 变量在读取之前被开动化过 变量值的类型正确

在考证阶段做这些查验意味着不需要在运行阶段做这些查验。链接阶段的查验降速了类加载的速率,关联词它幸免了试验这些字节码时的屡次查验。

准备经由包括为静态存储和 JVM 使用的数据结构(比如步骤表)分拨内存空间。静态变量创建并开动化为默许值,关联词开动化代码不在这个阶段试验,因为这是开动化经由的一部分。

领略是可选的阶段。它包括通过加载援用的类和接口来查验这些标记援用是否正确。要是不是发生在这个阶段,标记援用的领略要比及字节码辅导使用这个援用的时候才会进行。

类大略接口开动化由类或接口开动化步骤的试验构成。

 

JVM内幕:Java诬捏机详解

 

JVM 中有多个类加载器,分饰不同的脚色。每个类加载器由它的父加载器加载。bootstrap 加载器除外,它是通盘最顶层的类加载器。

Bootstrap 加载器一般由腹地代码终了,因为它在 JVM 加载以后的早期阶段就被开动化了。bootstrap 加载器真贵载入基础的 Java API,比如包含 rt.jar。它只加载领有较高信任级别的启动旅途下找到的类,因此跳过了好多普通类需要做的校验使命。 Extension 加载器加载了轨范 Java 延迟 API 中的类,比如 security 的延迟函数。 System 加载器是愚弄的默许类加载器,比如从 classpath 中加载愚弄类。 用户自界说类加载器也不错用来加载愚弄类。使用自界说的类加载器有好多异常的原因:运行时从头加载类大略把加载的类分隔为不同的组,典型的用法比如 web 处事器 Tomcat。

 

JVM内幕:Java诬捏机详解

 

加快类加载

分享类数据(CDS)是Hotspot JVM 5.0 的时候引入的新特点。在 JVM 装置经由中,装置进度会加载一系列中枢 JVM 类(比如 rt.jar)到一个分享的内存映射区域。CDS 减少了加载这些类需要的工夫,栽培了 JVM 启动的速率,允许这些类被不同的 JVM 实例分享,同期也减少了内存蹧跶。

步骤区在何处

The Java Virtual Machine Specification Java SE 7 Edition 中写得很了了:“尽管步骤区逻辑上属于堆的一部分,简短的终了不错选拔不合它进行回收和压缩。”。Oracle JVM 的 jconsle 清楚步骤区和 code cache 区被当做为非堆内存,而 OpenJDK 则清楚 CodeCache 被当做 VM 中对象堆(ObjectHeap)的一个颓丧的域。

Classloader 援用

通盘的类加载之后都包含一个加载本人的加载器的援用,反过来每个类加载器都包含它们加载的通盘类的援用。

运行时常量池

JVM 小气了一个按类型折柳的常量池,一个访佛于标记表的运行时数据结构。尽管它包含更大都据。Java 字节码需要数据。这个数据鄙俚因为太大弗成径直存储在字节码中,改步改玉的是存储在常量池中,字节码包含这个常量池的援用。运行时常量池被用来上头先容过的动态链接。

常量池中不错存储多种类型的数据:

数字型 字符串型 类援用型 域援用型 步骤援用

示例代码如下:

Object foo = new Object(); 

写成字节码将是底下这么:

0: new #2 // Class java/lang/Object 1: dup 2: invokespecial #3 // Method java/ lang/Object "<init>"( ) V 

new 操作码的后头紧随着操作数 #2 。这个操作数是常量池的一个索引,示意它指向常量池的第二个实体。第二个实体是一个类的援用,这个实体反过来援用了另一个在常量池中包含 UTF8 编码的字符串类名的实体(// Class java/lang/Object)。然后,这个标记援用被用来寻找 java.lang.Object 类。new 操作码创建一个类实例并开动化变量。新类实例的援用则被添加到操作数栈。dup 操作码创建一个操作数栈顶元素援用的稀罕拷贝。临了用 invokespecial 来调用第 2 行的实例开动化步骤。操作码也包含一个指向常量池的援用。开动化步骤把操作数栈出栈的顶部援用当做此步骤的一个参数。临了这个新对象只消一个援用,这个对象仍是完成了创建及开动化。

要是你编译底下的类:

package org.jvminternals; public class SimpleClass {    public void sayHello() {  System.out.println("Hello");  }   } 

生成的类文献常量池将是这个面孔:

 

JVM内幕:Java诬捏机详解

 

这个常量池包含了底下的类型:

 

JVM内幕:Java诬捏机详解

 

终点表

终点表像这么存储每个终点处理信息:

肇始点(Start point) 收尾点(End point) 终点处理代码的 PC 偏移量 被拿获终点的常量池索引

要是一个步骤有界说 try-catch 大略 try-finally 终点处理器,那么就会创建一个终点表。它为每个终点处理器和 finally 代码块存储必要的信息,包括处理器湮灭的代码块区域和处理终点的类型。

当步骤抛出终点时,JVM 会寻找匹配的终点处理器。要是莫得找到,那么步骤会立即收尾并弹出面前栈帧,这个终点会被从头抛到调用这个步骤的步骤中(在新的栈帧中)。要是通盘的栈帧都被弹出还莫得找到匹配的终点处理器,那么这个线程就会停止。要是这个终点在临了一个非防守进度抛出(比如这个线程是干线程),那么也有会导致 JVM 进度停止。

Finally 终点处理器匹配通盘的终点类型,且不管什么终点抛出 finally 代码块都会试验。在这种情况下,当莫得终点抛出时,finally 代码块照旧会在步骤临了试验。这种靠在代码 return 之前跳转到 finally 代码块来终了。

标记表

除了按类型来分的运行时常量池,Hotspot JVM 在永恒代还包含一个标记表。这个标记表是一个哈希表,保存了标记指针到标记的映射干系(也即是 Hashtable

援用计数被用来限度一个标记从标记表从移除的经由。比如当一个类被卸载时,它领有的在常量池中通盘标记的援用计数将减少。当标记表中的标记援用计数为 0 时,标记表会合计这个标记不再被援用,将从标记表中卸载。标记表和后头先容的字符串表都被保存在一个表率化的结构中,以便栽培成果并保证每个实例只出现一次。

字符串表

Java 讲话表率条件交流的(即包含交流序列的 Unicode 指针序列)字符串字面量必须指向交流的 String 实例。除此之外,在一个字符串实例上调用 String.intern() 步骤的复返援用必须与字符串是字面量时的同样。因此,底下的代码复返 true:

("j" + "v" + "m").intern() == "jvm" 

Hotspot JVM 中 interned 字符串保存在字符串表中。字符串表是一个哈希表,保存着对象指针到标记的映射干系(也即是Hashtable

当类加载时,字符串字面量被编译器自动 intern 并加入到标记表。除此之外,String 类的实例不错调用 String.intern() 显式地 intern。当调用 String.intern() 步骤时,要是标记表仍是包含了这个字符串,那么就会复返标记内外的这个援用,要是不是,那么这个字符串就被加入到字符串表中同期复返这个援用。



    热点资讯

    相关资讯