喜爱文章,动动手指导个赞
引言
敬爱读者你们好,对于jvm篇章的连载,后面三章讲了类加载器,本篇文章将进入jvm畛域的另一个知识点,java内存模型。彻底的理解java内存模型,是有必要的。只有把握了java的内存模型,内存空间分为哪些区域,能力更好地了解,java是如何创建对象以及如何调配对象的空间。对后续的jvm调优打下松软的根底。而对于当初的互联网行业来说,高并发,高可用曾经必不可少,而学好jvm调优,不仅能在企业工作当中针对高并发场景下的零碎进行优化,在日常对系统的谬误排查、零碎的优化也起着至关重要的作用。心愿这篇文章能让各位读者学到真正的本事。同时也感激大家的继续关注和认可。
一:JDK体系结构
JDK、JRE、JVM之间的关系
JDK:Java Development Kit(java开发工具包),蕴含JRE和开发工具包,例如javac、javah(生成实现本地办法所需的 C 头文件和源文件)。
JRE:Java Runtime Environment(java运行环境),蕴含JVM和类库。
JVM:Java Virtual Machine(Java虚拟机),负责执行符合规范的Class文件。
Java语言的跨平台个性
JVM所处的地位
(1)通常工作中所接触的根本是Java库和利用以及Java外围类库,晓得如何应用就能够了,然而归根结底代码都是要编译成class文件由Java虚拟机装载执行,所产生的后果或者景象都能够通过Java虚拟机的运行机制来解释。一些雷同的代码会因为虚拟机的实现不同而产生不同后果。
(2)在Java平台的构造中,能够看出,Java虚拟机(JVM)处在外围的地位,是程序与底层操作系统和硬件无关的要害。它的下方是移植接口,移植接口由两局部组成:适配器和Java操作系统,其中依赖于平台的局部称为适配器;JVM通过移植接口在具体的平台和操作系统上实现;在JVM的上方是Java的根本类库和扩大类库以及它们的API, 利用Java API编写的应用程序(application)和小程序(Java applet)能够在任何Java平台上运行而无需思考底层平台,就是因为有Java虚拟机(JVM)实现了程序与操作系统的拆散,从而实现了Java的平台无关性。
(3)对JVM标准的的形象阐明是一些概念的汇合,它们曾经在书《The Java Virtual Machine Specification》(《Java虚拟机标准》)中被具体地形容了;对JVM的具体实现要么是软件,要么是软件和硬件的组合,它曾经被许多生产厂商所实现,并存在于多种平台之上;运行Java程序的工作由JVM的运行期实例单个承当。
(4)JVM能够由不同的厂商来实现。因为厂商的不同必然导致JVM在实现上的一些不同,像国内就有驰名的TaobaoVM;然而JVM还是能够实现跨平台的个性,这就要归功于设计JVM时的体系结构了。
(5)JVM在它的生存周期中有一个明确的工作,那就是装载字节码文件,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有抉择的转换成机器码执行,即Java程序被执行。因而当Java程序启动的时候,就产生JVM的一个实例;当程序运行完结的时候,该实例也跟着隐没了。
Class字节码
编译后被Java虚拟机所执行的代码应用了一种平台中立(不依赖于特定硬件及操作系统的)的二进制格局来示意,并且常常(但并非相对)以文件的模式存储,因而这种格局被称为Class文件格式。Class文件格式中准确地定义了类与接口的示意模式,包含在平台相干的指标文件格式中一些细节上的常规,
正如概念所说,Java为了可能实现平台无关性,制订了一套本人的二进制格局,并常常以文件的形式存储,称为Class文件。这样在不同平台上,只有都装置了Java虚拟机,具备Java运行环境[JRE],那么都能够运行雷同的Class文件。
上图形容了Java程序运行的一个全过程,也能够看出Java平台由Java虚拟机和Java利用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序能够运行在这个平台上。
由Java源文件编译生成字节码文件,这个过程非常复杂,学过《编译原理》的敌人都晓得必须通过词法剖析、语法分析、语义剖析、两头代码生成、代码优化等;同样的,Java源文件到字节码的生成也想要经验这些步骤。Javac编译器的最初工作就是调用con.sun.tools.javac.jvm.Gen类将这课语法树编译为Java字节码文件。
其实,所谓的编译字节码,无非就是将合乎Java语法标准的Java代码转化为合乎JVM标准的字节码文件。JVM的架构模型是基于栈的,大部分都须要通过栈来实现。
字节码构造比拟非凡,其外部不蕴含任何的分隔符,无奈人工辨别段落(字节码文件自身就是给机器读的),所以无论是字节程序、数量都是有严格规定的,所有16位、32位、64位长度的数据都将结构成2个、4个、8个-----8位字节单位来示意,多字节数据项总是依照Big-endian程序(高位字节在地址的最低位,位置字节在地址的最高位)来进行存储。
参考《Java虚拟机标准 Java SE7版》的形容,每一个字节码其实都对应着全局惟一的一个类或者接口的定义信息。字节码文件才用的是一种相似于C语言构造体的伪构造来形容字节码文件格式。字节码文件中对应的“根本类型”u1,u2,u4,u8别离示意无符号1、2、4、8个字节。
Class文件----总体格局
值得一提的是,一个无效的class字节码文件的前4个字节为0xCAFEBABE,都是固定的,被称为“魔术”,即magic。它就是JVM用于校验所读取的指标文件是否是一个无效且非法的字节码文件。由此可见,JVM并不是通过判断文件后缀名的形式来校验,以避免人为手动批改。
JVM底层架构图
下面这张图,是自己花了很多心理总结进去的,根本涵盖了java内存模型的构造。明天奉上。这篇文章会把下面这张图讲清楚。
运行时数据区:
1,堆
Java堆在虚拟机启动的时候被创立,Java堆次要用来为类实例对象和数组分配内存。Java虚拟机标准并没有规定对象在堆中的模式。
在Java中,堆被划分成两个不同的区域:新生代( Young )、老年代( Old );这也就是JVM采纳的“分代收集算法”,简略说,就是针对不同特色的java对象采纳不同的 策略施行寄存和回收,天然所用分配机制和回收算法就不一样。新生代( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
分代收集算法:采纳不同算法解决[寄存和回收]Java刹时对象和短暂对象。大部分Java对象都是刹时对象,朝生夕灭,存活很短暂,通常寄存在Young新生代,采纳复制算法对新生代进行垃圾回收。老年代对象的生命周期个别都比拟长,极其状况下会和JVM生命周期保持一致;通常采纳标记-压缩算法对老年代进行垃圾回收。
这样划分的目标是为了使JVM可能更好的治理堆内存中的对象,包含内存的调配以及回收。
Java堆可能产生如下异常情况:如果理论所需的堆超过了主动内存管理系统能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError异样。简称(OOM)。
堆大小 = 新生代 + 老年代。堆的大小可通过参数–Xms(堆的初始容量)、-Xmx(堆的最大容量) 来指定。
其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域别离被命名为 from 和 to,以示辨别。默认的,Edem : from : to = 8 : 1 : 1 。(能够通过参数 –XX:SurvivorRatio 来设定 。
即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会应用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是闲暇着的。
新生代理论可用的内存空间为 9/10 ( 即90% )的新生代空间。
java堆是GC垃圾回收的次要区域。 GC分为两种: Minor GC、Full GC(也叫做Major GC)
Minor GC(简称GC)
Minor GC是产生在新生代中的垃圾收集动作, 所采纳的是复制算法。
GC个别为堆空间某个区产生了垃圾回收,
新生代(Young)简直是所有java对象出世的中央。即java对象申请的内存以及寄存都是在这个中央。java中的大部分对象通常不会短暂的存活, 具备朝生夕死的特点。
当一个对象被断定为“死亡”的时候, GC就有责任来回收掉这部分对象的内存空间。
新生代是收集垃圾的频繁区域。
2,办法区(元空间)
办法区在虚拟机启动的时候被创立,它存储了每一个类的构造信息,例如运行时常量池、字段和办法数据、构造函数和一般办法的字节码内容、还包含在类、实例、接口初始化时用到的非凡办法。
办法区可能产生如下异常情况: 如果办法区的内存空间不能满足内存调配申请,那Java虚拟机将抛出一个OutOfMemoryError异样.
3,JVM栈空间
每个Java虚拟机线程都有本人的Java虚拟机栈。Java虚拟机栈用来寄存栈帧,而栈帧次要包含了:局部变量表、操作数栈、动静链接。Java虚拟机栈容许被实现为固定大小或者可动静扩大的内存大小。
Java虚拟机应用局部变量表来实现办法调用时的参数传递。局部变量表的长度在编译期曾经决定了并存储于类和接口的二进制示意中,一个局部变量能够保留一个类型为boolean、byte、char、short、float、reference和returnAddress的数据,两个局部变量能够保留一个类型为long和double的数据。
Java虚拟机提供一些字节码指令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于从操作数栈取走数据、操作数据和把操作后果从新入栈。在办法调用的时候,操作数栈也用来筹备调用办法的参数以及接管办法返回后果。
每个栈帧中都蕴含一个指向运行时常量区的援用反对以后办法的动静链接。在Class文件中,办法调用和拜访成员变量都是通过符号援用来示意的,动静链接的作用就是将符号援用转化为理论办法的间接援用或者拜访变量的运行是内存地位的正确偏移量。
总的来说,Java虚拟机栈是用来寄存局部变量和过程后果的中央。
Java虚拟机栈可能产生如下异常情况: 如果Java虚拟机栈被实现为固定大小内存,线程申请调配的栈容量超过Java虚拟机栈容许的最大容量时,Java虚拟机将会抛出一个StackOverflowError异样。
如果Java虚拟机栈被实现为动静扩大内存大小,并且扩大的动作曾经尝试过,然而目前无奈申请到足够的内存去实现扩大,或者在建设新的线程时没有足够的内存去创立对应的虚拟机栈,那Java虚拟机将会抛出一个OutOfMemoryError异样。
1.符号援用(Symbolic References):
符号援用以一组符号来形容所援用的指标,符号能够是任何模式的字面量,只有应用时可能无歧义的定位到指标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量呈现。符号援用与虚拟机的内存布局无关,援用的指标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所援用的类的理论地址,因而只能应用符号援用来代替。比方org.simple.People类援用了org.simple.Language类,在编译时People类并不知道Language类的理论内存地址,因而只能应用符号org.simple.Language(假如是这个,当然理论中是由相似于CONSTANT_Class_info的常量来示意的)来示意Language类的地址。各种虚拟机实现的内存布局可能有所不同,然而它们能承受的符号援用都是统一的,因为符号援用的字面量模式明确定义在Java虚拟机标准的Class文件格式中。
2.间接援用:
间接援用能够是
(1)间接指向指标的指针(比方,指向“类型”【Class对象】、类变量、类办法的间接援用可能是指向办法区的指针)
(2)绝对偏移量(比方,指向实例变量、实例办法的间接援用都是偏移量)
(3)一个能间接定位到指标的句柄
间接援用是和虚拟机的布局相干的,同一个符号援用在不同的虚拟机实例上翻译进去的间接援用个别不会雷同。如果有了间接援用,那援用的指标必然曾经被加载入内存中了。
4,本地办法栈
对于一个运行中的Java程序而言,它还可能会用到一些跟本地办法相干的数据区。当某个线程调用一个本地办法时,它就进入了一个全新的并且不再受虚拟机限度的世界。本地办法能够通过本地办法接口来拜访虚拟机的运行时数据区,但不止如此,它还能够做任何它想做的事件。
本地办法实质上时依赖于实现的,虚拟机实现的设计者们能够自在地决定应用怎么的机制来让Java程序调用本地办法。
任何本地办法接口都会应用某种本地办法栈。当线程调用Java办法时,虚构机会创立一个新的栈帧并压入Java栈。然而当它调用的是本地办法时,虚构机会放弃Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简略地动静连贯并间接调用指定的本地办法。
如果某个虚拟机实现的本地办法接口是应用C连贯模型的话,那么它的本地办法栈就是C栈。当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的程序压入栈,它的返回值也以确定的形式传回调用者。同样,这就是虚拟机实现中本地办法栈的行为。
很可能本地办法接口须要回调Java虚拟机中的Java办法,在这种状况下,该线程会保留本地办法栈的状态并进入到另一个Java栈。
下图描述了这样一个情景,就是当一个线程调用一个本地办法时,本地办法又回调虚拟机中的另一个Java办法。
这幅图展现了JAVA虚拟机外部线程运行的全景图。一个线程可能在整个生命周期中都执行Java办法,操作它的Java栈;或者它可能毫无阻碍地在Java栈和本地办法栈之间跳转。
该线程首先调用了两个Java办法,而第二个Java办法又调用了一个本地办法,这样导致虚拟机应用了一个本地办法栈。假如这是一个C语言栈,其间有两个C函数,第一个C函数被第二个Java办法当做本地办法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过本地办法接口回调了一个Java办法(第三个Java办法),最终这个Java办法又调用了一个Java办法(它成为图中的以后办法)。
Navtive 办法是 Java 通过 JNI 间接调用本地 C/C++ 库,能够认为是 Native 办法相当于 C/C++ 裸露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 办法。当线程调用 Java 办法时,虚构机会创立一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 办法时,虚构机会放弃 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简略地动静连贯并间接调用指定的 native 办法。
5,程序计数器
程序计数器是一个记录着以后线程所执行的字节码的行号指示器。
JAVA代码编译后的字节码在未通过JIT(实时编译器)编译前,其执行形式是通过“字节码解释器”进行解释执行。简略的工作原理为解释器读取装载入内存的字节码,依照程序读取字节码指令。读取一个指令后,将该指令“翻译”成固定的操作,并依据这些操作进行分支、循环、跳转等流程。
从下面的形容中,可能会产生程序计数器是否是多余的疑难。因为沿着指令的程序执行上来,即便是分支跳转这样的流程,跳转到指定的指令处按程序继续执行是齐全可能保障程序的执行程序的。假如程序永远只有一个线程,这个疑难没有任何问题,也就是说并不需要程序计数器。但实际上程序是通过多个线程协同单干执行的。
首先咱们要搞清楚JVM的多线程实现形式。JVM的多线程是通过CPU工夫片轮转(即线程轮流切换并调配处理器执行工夫)算法来实现的。也就是说,某个线程在执行过程中可能会因为工夫片耗尽而被挂起,而另一个线程获取到工夫片开始执行。当被挂起的线程从新获取到工夫片的时候,它要想从被挂起的中央继续执行,就必须晓得它上次执行到哪个地位,在JVM中,通过程序计数器来记录某个线程的字节码执行地位。因而,程序计数器是具备线程隔离的个性,也就是说,每个线程工作时都有属于本人的独立计数器。
程序计数器的特点
1.线程隔离性,每个线程工作时都有属于本人的独立计数器。
2.执行java办法时,程序计数器是有值的,且记录的是正在执行的字节码指令的地址(参考上一大节的形容)。
3.执行native本地办法时,程序计数器的值为空(Undefined)。因为native办法是java通过JNI间接调用本地C/C++库,能够近似的认为native办法相当于C/C++裸露给java的一个接口,java通过调用这个接口从而调用到C/C++办法。因为该办法是通过C/C++而不是java进行实现。那么天然无奈产生相应的字节码,并且C/C++执行时的内存调配是由本人语言决定的,而不是由JVM决定的。
4.程序计数器占用内存很小,在进行JVM内存计算时,能够忽略不计。
5.程序计数器,是惟一一个在java虚拟机标准中没有规定任何OutOfMemoryError的区域。
6,线程栈
线程堆栈也称线程调用堆栈,是虚拟机中线程(包含锁)状态的一个霎时状态的快照,即零碎在某一个时刻所有线程的运行状态,包含每一个线程的调用堆栈,锁的持有状况。尽管不同的虚拟机打印进去的格局有些不同,然而线程堆栈的信息都蕴含:
1、线程名字,id,线程的数量等。
2、线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程在期待锁等)
3、调用堆栈(即函数的调用档次关系)调用堆栈蕴含残缺的类名,所执行的办法,源代码的行数。
因为线程栈是刹时快照蕴含线程状态以及调用关系,所以借助堆栈信息能够帮忙剖析很多问题,比方线程死锁,锁争用,死循环,辨认耗时操作等等。线程栈是刹时记录,所以没有历史音讯的回溯,个别咱们都须要联合程序的日志进行跟踪,个别线程栈能剖析如下性能问题:
1、零碎平白无故的cpu过高
2、零碎挂起,无响应
3、零碎运行越来越慢
4、性能瓶颈(如无奈充分利用cpu等)
5、线程死锁,死循环等
6、因为线程数量太多导致的内存溢出(如无奈创立线程等)
线程栈状态
线程栈状态有如下几种
1、NEW
2、RUNNABLE
3、BLOCKED
4、WAITING
5、TIMED_WAITING
6、TERMINATED
上面顺次对6种线程栈状态进行介绍。
1、NEW
线程刚刚被创立,也就是曾经new过了,然而还没有调用start()办法,这个状态咱们应用jstack进行线程栈dump的时候根本看不到,因为是线程刚创立时候的状态。
2、RUNNABLE
从虚拟机的角度看,线程正在运行状态,状态是线程正在失常运行中, 当然可能会有某种耗时计算/IO期待的操作/CPU工夫片切换等, 这个状态下产生的期待个别是其余系统资源, 而不是锁, Sleep等。
处于RUNNABLE状态的线程是不是肯定会耗费cpu呢,不肯定,像socket IO操作,线程正在从网络上读取数据,只管线程状态RUNNABLE,但实际上网络io,线程绝大多数工夫是被挂起的,只有当数据达到后,线程才会被唤起,挂起产生在本地代码(native)中,虚拟机基本不统一,不像显式的调用sleep和wait办法,虚拟机能力晓得线程的真正状态,但在本地代码中的挂起,虚拟机无奈晓得真正的线程状态,因而一律显示为RUNNABLE。
3、BLOCKED
线程处于阻塞状态,正在期待一个monitor lock。通常状况下,是因为本线程与其余线程专用了一个锁。其余在线程正在应用这个锁进入某个synchronized同步办法块或者办法,而本线程进入这个同步代码块也须要这个锁,最终导致本线程处于阻塞状态。
实在生存例子:
明天你要去阿里面试。这是你幻想的工作,你曾经盯着它多年了。你早上起来,筹备好,穿上你最好的外衣,对着镜子打理好。当你走进车库发现你的敌人曾经把车开走了。在这个场景,你只有一辆车,所以怎么办?在实在生存中,可能会打架抢车。 当初因为你敌人把车开走了你被BLOCKED了。你不能去加入面试。
这就是BLOCKED状态。用技术术语讲,你是线程T1,你敌人是线程T2,而锁是车。T1被BLOCKED在锁(例子里的车)上,因为T2曾经获取了这个锁。
4、WAITING
这个状态下是指线程领有了某个锁之后, 调用了他的wait办法, 期待其余线程/锁拥有者调用 notify / notifyAll一遍该线程能够持续下一步操作, 这里要辨别 BLOCKED 和 WATING 的区别, 一个是在临界点里面期待进入, 一个是在了解点外面wait期待他人notify, 线程调用了join办法 join了另外的线程的时候, 也会进入WAITING状态, 期待被他join的线程执行完结,处于waiting状态的线程根本不耗费CPU。
实在生存例子:
再看下几分钟后你的敌人开车回家了,锁(车)就被开释了,当初你意识到快到面试工夫了,而开车过来很远。所以你拼命地踩油门。限速120KM/H而你以160KM/H的速度在开。很可怜,一个交警发现你超速了,让你停到路边。当初你进入了WAITING状态。你停下车坐在那等着交警过去查看开罚单而后给你放行。基本上,你只有等他让你走(你没法开车逃),你被卡在WAITING状态了。
用技术术语来讲,你是线程T1而交警是线程T2。你开释你的锁(例子中你停下了车),并进入WAITING状态,直到警察(例子中T2)让你走,你陷入了WAITING状态。
5、TIMED_WAITING
该线程正在期待,通过应用了 sleep, wait, join 或者是 park 办法。(这个与 WAITING 不同是通过办法参数指定了最大等待时间,WAITING 能够通过工夫或者是内部的变动解除),线程期待指定的工夫。
实在生存例子:
只管这次面试过程充斥戏剧性,但你在面试中做的十分好,惊艳了所有人并取得了高薪工作。你回家通知你的街坊你的新工作并表白你冲动的情绪。你的敌人通知你他也在同一个办公楼里工作。他倡议你坐他的车去下班。你想这不错。所以去阿里下班的第一天,你走到你街坊的房子,在他的房子前停好你的车。你等了他10分钟,但你的街坊没有呈现。你而后持续开本人的车去下班,这样你不会在第一天就早退。这就是TIMED_WAITING.
用技术术语来解释,你是线程T1而你的街坊是线程T2。你开释了锁(这里是进行开车)并等了足足10分钟。如果你的街坊T2没有来,你持续开车(老司机留神车速,其余乘客记得买票)。
6、TERMINATED
线程终止,同样咱们在应用jstack进行线程dump的时候也很少看到该状态的线程栈。
1.局部变量表
局部变量表(Local Variable Table)是一组变量值存储空间,用于寄存办法参数和办法内定义的局部变量。局部变量表的容量以变量槽(Variable Slot)为最小单位,Java虚拟机标准并没有定义一个槽所应该占用内存空间的大小,然而规定了一个槽应该能够寄存一个32位以内的数据类型。
在Java程序编译为Class文件时,就在办法的Code属性中的max_locals数据项中确定了该办法所需调配的局部变量表的最大容量。(最大Slot数量)
一个局部变量能够保留一个类型为boolean、byte、char、short、int、float、reference和returnAddress类型的数据。reference类型示意对一个对象实例的援用。returnAddress类型是为jsr、jsr_w和ret指令服务的,目前曾经很少应用了。
虚拟机通过索引定位的办法查找相应的局部变量,索引的范畴是从0~局部变量表最大容量。如果Slot是32位的,则遇到一个64位数据类型的变量(如long或double型),则会间断应用两个间断的Slot来存储。
2.操作数栈
操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出栈(LIFO)。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到办法的Code属性的max_stacks数据项中。
操作数栈的每一个元素能够是任意Java数据类型,32位的数据类型占一个栈容量,64位的数据类型占2个栈容量,且在办法执行的任意时刻,操作数栈的深度都不会超过max_stacks中设置的最大值。
当一个办法刚刚开始执行时,其操作数栈是空的,随着办法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给办法调用者,也就是出栈/入栈操作。一个残缺的办法执行期间往往蕴含多个这样出栈/入栈的过程。
3.动静连贯
在一个class文件中,一个办法要调用其余办法,须要将这些办法的符号援用转化为其在内存地址中的间接援用,而符号援用存在于办法区中的运行时常量池。
Java虚拟机栈中,每个栈帧都蕴含一个指向运行时常量池中该栈所属办法的符号援用,持有这个援用的目标是为了反对办法调用过程中的动静连贯(Dynamic Linking)。
这些符号援用一部分会在类加载阶段或者第一次应用时就间接转化为间接援用,这类转化称为动态解析。另一部分将在每次运行期间转化为间接援用,这类转化称为动静连贯。
4.动态链接
动态链接的过程就曾经把要链接的内容曾经链接到了生成的可执行文件中,就算你在去把动态库删除也不会影响可执行程序的执行;而动静链接这个过程却没有把内容链接进去,而是在执行的过程中,再去找要链接的内容,生成的可执行文件中并没有要链接的内容,所以当你删除动静库时,可执行程序就不能运行。
艰深解释:动态连贯库就是把(lib)文件中用到的函数代码间接链接进目标程序,程序运行的时候不再须要其它的库文件;动静链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的地位等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因而须要相应DLL文件的反对。
这篇内容次要介绍一下图中的概念。下篇文章我会把这些概念串起来,比如说创建对象的过程,内存空间是怎么工作的。感激大家的继续关注。
另外我在我的公众号内,针对JVM写了一个系列介绍内容,想要获取更多内容,请关注公众号:奇客工夫