Java虚拟机(JVM)知多少
本文大量参考:https://www.cnblogs.com/lfs2640666960/p/9297176.html
概述
JVM是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。所以,JAVA虚拟机JVM是属于JRE的,而现在我们安装JDK时也附带安装了JRE(当然也可以单独安装JRE)。
参考:https://blog.csdn.net/stanlee_0/article/details/51171382
0. JVM与进程的关系
简单理解,一个启动的JVM实例相当于操作系统中工作的一个进程,杀死这个进程,JVM实例也停止工作。
虚拟机是运行在操作系统之中的,那么什么东西才能在操作系统中运行呢?当然是进程,因为进程是操作系统中的执行单位。当我们启动一个Java应用程序时,实际是在系统中启动一个JVM实例,由这个JVM实例负责加载执行管理.class文件。不同的进程(JVM实例)间是相互隔离的。一个JVM实例内可以启动多个线程,线程间可以共享部分资源(堆内存、元数据区),多线程并发编程指的是在同一个JVM实例内同时启动多个线程实例并发工作。
1. 一次编译,到处运行
从软件架构的角度来看,如果需要实现不同平台的兼容性,有效办法便是加一个中间层进行封装,在Java世界中,JVM就是这个中间层。JVM屏蔽了平台间的差异,针对不同的操作系统开发了不同的本地方法栈,对上层暴露出同意的接口,使得在Windows系统编写的Java代码编译后能在linux系统上部署的JVM里正常运行,开发人员只需完成代码开发编译成.class文件的JVM字节码,即可实现一次编译,到处运行。
1.1 Java源码编译由以下三个过程组成:
- 分析和输入到符号表
- 注解处理
- 语义分析和生成class文件
class文件为8位字节码(ByteCode)组成,Java所有指令有200个左右,8位可以表示256种指令信息。前4个字节CAFE BABE为Gosling定义的一个魔法数,标志位一个Java类文件,缺失此魔法数则表示该文件不是一个Java文件或者文件已损坏。紧接着的4个字节为JDK的版本号,0x34为52,对应JDK1.8.0。
.class文件中的字节码
Java代码编译过程
1.2 编译时期-语法糖
语法糖可以看做是编译器实现的一些“小把戏”,这些“小把戏”可能会使得效率“大提升”。
泛型是最常见的语法糖:
- 泛型只在Java源码中存在,编译后会被替换为原来的原生类型。这个过程也被称为:泛型擦除。
泛型的作用:
- 代码更加简洁【不用强制转换】
- 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
- 可读性和稳定性【在编写集合的时候,就限定了类型】
那么从.class文件到最终的代码执行,JVM到底进行了哪些工作,下面从代码的加载出发,详细描述。
2. JVM的加载class文件
2.1 JVM加载class文件的策略
虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行“初始化”(class文件加载到JVM中):
- 创建类的实例(new 的方式)。访问某个类或接口的静态变量,或者对该静态变量赋值,调用类的静态方法
- 反射的方式
- 初始化某个类的子类,则其父类也会被初始化
- Java虚拟机启动时被标明为启动类的类,直接使用java.exe命令来运行某个主类(包含main方法的那个类)
- 当使用JDK1.7的动态语言支持时(....)
所以JVM是动态加载java类的:
- 优先加载保证程序运行的基础类(像是基类)到jvm中,至于其他类,则在需要的时候才加载,节省内存开销。
2.2 JVM的类加载器
2.2.1 各个加载器的工作责任:
- 1)Bootstrap ClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
- 2)Extension(Platform) ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
- 3)App ClassLoader:负责记载classpath中指定的jar包及目录中class
2.2.2 双亲委派模式
- 1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
- 2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
- 3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
- 4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载
- 5、如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException
简单来说:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。
双亲委派模式的优点:
- 防止内存中出现多份同样的字节码(安全性角度)
特别说明:
- 类加载器在成功加载某个类之后,会把得到的
java.lang.Class
类的实例缓存起来。下次再请求加载该类时,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。
2.2.3 类加载详细过程
加载器加载到jvm中,接下来其实又分了好几个步骤:
- 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象。
- 连接,连接又包含三块内容:验证、准备、初始化。
1)验证,文件格式、元数据、字节码、符号引用验证;
2)准备,为类的静态变量分配内存,并将其初始化为默认值;
3)解析,把类中的符号引用转换为直接引用
- 初始化,为类的静态变量赋予正确的初始值。
2.3 class文件执行模式
字节码由类加载器加载到JVM环境后,有三种执行模式:
第一种:解释执行;
第二种:JIT编译执行;将代码转换成机器码,直接交给CPU执行,提高执行效率;
第三种:解释与JIT编译混合执行(主流JVM默认执行方式)。
由于混合执行在机器启动时以解释执行为主,执行效率会低于经过JIT动态编译热点代码的热机,冷机能承受负载要小于热机,在发布切流时需注意此差别可能造成冷机过载假死。
- 编译也是要花费时间的,我们一般对热点代码做编译,非热点代码直接解析就好了。
热点代码解释:一、多次调用的方法。二、多次执行的循环体
使用热点探测来检测是否为热点代码,热点探测有两种方式:
- 采样
- 计数器
目前HotSpot使用的是计数器的方式,它为每个方法准备了两类计数器:
- 方法调用计数器(Invocation Counter)
- 回边计数器(Back EdgeCounter)。
- 在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。
详情参考:
- https://www.mrsssswan.club/2018/06/30/jvm-start1/---浅解JVM加载class文件
- https://zhuanlan.zhihu.com/p/28476709---JVM杂谈之JIT
扩展阅读:
- https://www.ibm.com/developerworks/cn/java/j-lo-classloader/---深入探讨 Java 类加载器
- https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/---深入浅出 JIT 编译器
- https://www.zhihu.com/question/46719811---Java 类加载器(ClassLoader)的实际使用场景有哪些?
3 JVM内存模型
类加载进虚拟机后,会为新生对象分配内存。
3.1 Java对象内存花费:
(1)每个Java对象,都有一个对象头,占用16字节,包含一些对象的元信息,比如指向他的类的指针。
如果对象本身很小,比如int,但是他的对象头比对象自己还大。
(2)Java的String对象,会比他内存的原始数据,多出40个字节。
String内部使用的char数组来保存内部的字符串序列,并且还要保存诸如输出长度之类的信息。
char使用的是UTF-16编码,每个字符会占2个字节。比如,包含10个字符的String,2*10+40=60字节
(3)Java中的集合类型,比如HashMap和LinkedList,内部使用链表数据结构。
链表中的每个数据,使用Entry对象包装。
Entry对象,不光有对象头,还有指向下一个Entry的指针,占用8字节。
(4)元素类型为原始数据类型(int),内部通常会使用原始数据类型的包装类型(Integer)来存储元素。
3.2 Heap(堆区)
Heap是OOM(OutOfMemory)的主要发源地,它存储着几乎所有的实例对象,堆由垃圾收集器自动回收,被各子线程共享。
堆主要分为两大块:新生代和老年代。Eden为新对象的出生区,当Eden区填满时会出发YGC(Young Garbage Collection),将依然存活的对象送往Survivor区(S0|S1)。S0|S1在每次清理时会将存活对象整理到未使用的空间,然后清除当前使用的空间,可以减少内存碎片化。老年代于保存超过YGC次数阈值的对象以及超大对象。当老年代无法存放更多对象时会触发FGC(Full Garbage Collection),如果依然无法放下,则抛出OOM。
3.3 JVM Stack(虚拟机栈)
虚拟机栈是描述Java方法执行的内存区域,是线程私有的。线程是CPU执行任务的最小单位,任一时刻一个CPU内核只能运行一个线程的一条指令。方法的调用开始到执行完成的过程,就是栈桢从入栈到出栈的过程。在线程活动中,只有位于栈顶的桢才是有效的,称为当前帧,正在执行的方法称为当前方法,所有指令都只能对当前栈桢进行操作。StackOverflowError表示栈溢出,常出现在递归方法中。
3.4 Metaspace(元空间)
以Hotspot(JVM)为例,在JDK7及之前版本中有Perm(永久代)区,在启动时固定大小,难以调优。在某些动态加载类过多的场景,易发生Perm区的OOM。此外,永久代在垃圾回收过程中还存在诸多问题。所以,在JDK8中用元空间替代。
元空间不同于永久代,它在本地内存中分配。在JDK8中,Perm区的字符串常量移至堆内存,其他如类元信息、字段、静态属性、方法、常量等移至元空间。
常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放--->来源:深入理解Java虚拟机 JVM高级特性与最佳实践(第二版)
****注:关于String的加载过程有疑虑,需进一步了解。
HotSpot VM里,记录interned string的一个全局表叫做StringTable,它本质上就是个HashSet<String>。注意它只存储对java.lang.String实例的引用,而不存储String对象的内容
如下例中test2的结果不能详解。
@Test
public void test1() {
System.out.println("-----*******test1*********-----"); String s = new String("1");
s.intern();
String s1 = "1";
System.out.println(s == s1); // false String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11"; System.out.println(s3 == s4); // false
} @Test
public void test2() {
System.out.println("-----*******test2*********-----"); String s1 = new String("he") + new String("llo");
String s2 = new String("h") + new String("ello");
String s5 = "hello"; String s3 = s1.intern();
String s4 = s2.intern(); System.out.println(s1 == s3); // false
System.out.println(s1 == s4); // false
System.out.println(s1 == s2); // false
System.out.println(s1 == s5); // false
System.out.println(s2 == s5); // false
} @Test
public void test2() {
System.out.println("-----*******test2*********-----"); String s1 = new String("he") + new String("llo");
String s2 = new String("h") + new String("ello");
// String s5 = "hello"; String s3 = s1.intern();
String s4 = s2.intern(); System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true
System.out.println(s1 == s2); // false
// System.out.println(s1 == s5); // false
// System.out.println(s2 == s5); // false
}
3.5 本地方法栈&程序计数器
本地方法栈主要为本地(Native)方法服务,本地方法通过JNI(Java Native Interface)来访问虚拟机运行时数据区,具有和JVM相同的权限和能力。一般不建议大量实现JNI,易丧失跨平台特性,影响稳定性。
程序计数器用来存放执行指令的偏移量和行号指示器等,线程执行或回复都要依赖程序计数器。
4. GC垃圾回收
4.1JVM垃圾回收简单介绍
在C++中,我们知道创建出的对象是需要手动去delete掉的。我们Java程序运行在JVM中,JVM可以帮我们“自动”回收不需要的对象。
首先,JVM回收的是垃圾,垃圾就是我们程序中已经是不需要的了。垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”。判断哪些对象“死去”常用有两种方式:
- 引用计数法-->这种难以解决对象之间的循环引用的问题
- 可达性分析算法-->主流的JVM采用的是这种方式
4.2 JVM垃圾回收算法
现在已经可以判断哪些对象已经“死去”了,我们现在要对这些“死去”的对象进行回收,回收也有好几种算法:
- 标记-清除算法
- 复制算法
- 标记-整理算法
- 分代收集算法
无论是可达性分析算法,还是垃圾回收算法,JVM使用的都是准确式GC。JVM是使用一组称为OopMap的数据结构,来存储所有的对象引用(这样就不用遍历整个内存去查找了,时间换空间)。
并且不会将所有的指令都生成OopMap,只会在安全点上生成OopMap,在安全区域上开始GC。
- 在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举(可达性分析)。
4.3 常用垃圾收集器
上面所讲的垃圾收集算法只能算是方法论,落地实现的是垃圾收集器:
- Serial收集器
- ParNew收集器
- Parallel Scavenge收集器
- Serial Old收集器
- Parallel Old收集器
- CMS收集器
- G1收集器
上面这些收集器大部分是可以互相组合使用的。 垃圾收集器主要关注空间碎片和STW(Stop The World:STW执行时会暂停整个应用程序的执行)影响性能的问题。Hotspot新一代的垃圾回收器G1具备压缩功能,能避免碎片问题,且其暂停时间更加可控。
参考资料:
- http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html---JVM系列三:JVM参数设置、分析
5. JVM面试题
5.1详细jvm内存模型
根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。
具体可能会聊聊jdk1.7以前的PermGen(永久代),替换成Metaspace(元空间)
- 原本永久代存储的数据:符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap
- Metaspace(元空间)存储的是类的元数据信息(metadata)
- 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
- 替换的好处:一、字符串存在永久代中,容易出现性能问题和内存溢出。二、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
5.2讲讲什么情况下回出现内存溢出,内存泄漏?
内存泄漏的原因很简单:
- 对象是可达的(一直被引用)
- 但是对象不会被使用
public static void main(String[] args) {
Set set = new HashSet();
for (int i = 0; i < 10; i++) {
Object object = new Object();
set.add(object); // 设置为空,这对象我不再用了
object = null;
}
// 但是set集合中还维护这obj的引用,gc不会回收object对象
System.out.println(set);
}
解决这个内存泄漏问题也很简单,将set设置为null,那就可以避免上诉内存泄漏问题了。其他内存泄漏得一步一步分析了。
内存泄漏参考资料:
内存溢出的原因:
- 内存泄露导致堆栈内存不断增大,从而引发内存溢出。
- 大量的jar,class文件加载,装载类的空间不够,溢出
- 操作大量的对象导致堆内存空间已经用满了,溢出
- nio直接操作内存,内存过大导致溢出
解决:
- 查看程序是否存在内存泄漏的问题
- 设置参数加大空间
- 代码中是否存在死循环或循环产生过多重复的对象实体、
- 查看是否使用了nio直接操作内存。
参考资料:
5.3说说线程栈
这里的线程栈应该指的是虚拟机栈吧...
JVM规范让每个Java线程拥有自己的独立的JVM栈,也就是Java方法的调用栈。
当方法调用的时候,会生成一个栈帧。栈帧是保存在虚拟机栈中的,栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息
线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。
通过jstack工具查看线程状态
参考资料:
- http://wangwengcn.iteye.com/blog/1622195
- https://www.cnblogs.com/Codenewbie/p/6184898.html
- https://blog.csdn.net/u011734144/article/details/60965155
5.4JVM 年轻代到年老代的晋升过程的判断条件是什么呢?
- 部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代。
- 如果对象的大小大于Eden的二分之一会直接分配在old,如果old也分配不下,会做一次majorGC,如果小于eden的一半但是没有足够的空间,就进行minorgc也就是新生代GC。
- minor gc后,survivor仍然放不下,则放到老年代
- 动态年龄判断 ,大于等于某个年龄的对象超过了survivor空间一半 ,大于等于某个年龄的对象直接进入老年代
5.5JVM 出现 fullGC 很频繁,怎么去线上排查问题
这题就依据full GC的触发条件来做:
- 如果有perm gen的话(jdk1.8就没了),要给perm gen分配空间,但没有足够的空间时,会触发full gc。
- 所以看看是不是perm gen区的值设置得太小了。
System.gc()
方法的调用
- 这个一般没人去调用吧~~~
- 当统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间,则会触发full gc(这就可以从多个角度上看了)
- 是不是频繁创建了大对象(也有可能eden区设置过小)(大对象直接分配在老年代中,导致老年代空间不足--->从而频繁gc)
- 是不是老年代的空间设置过小了(Minor GC几个对象就大于老年代的剩余空间了)
5.6类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?
双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。
- 假设有一个开发者自己编写了一个名为
java.lang.Object
的类,想借此欺骗JVM。现在他要使用自定义ClassLoader
来加载自己编写的java.lang.Object
类。 - 然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在
Bootstrap ClassLoader
的路径下找到java.lang.Object
类,并载入它
Java的类加载是否一定遵循双亲委托模型?
- 在实际开发中,我们可以通过自定义ClassLoader,并重写父类的loadClass方法,来打破这一机制。
- SPI就是打破了双亲委托机制的(SPI:服务提供发现)。SPI资料:
- https://zhuanlan.zhihu.com/p/28909673
- https://www.cnblogs.com/huzi007/p/6679215.html
- https://blog.csdn.net/sigangjun/article/details/79071850
参考资料:
5.7类的实例化顺序
- 1. 父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
- 2. 子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
- 3. 父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行
- 4. 父类构造方法
- 5. 子类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行
- 6. 子类构造方法
5.8JVM垃圾回收机制,何时触发MinorGC等操作
当young gen中的eden区分配满的时候触发MinorGC(新生代的空间不够放的时候).
5.9JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
这题不是很明白意思(水平有限...如果知道这题的意思可在评论区留言呀~~)
- 因为按我的理解:执行fgc是不会执行ygc的呀~~
YGC和FGC是什么
- YGC :对新生代堆进行gc。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。
- FGC :全堆范围的gc。默认堆空间使用到达80%(可调整)的时候会触发fgc。以我们生产环境为例,一般比较少会触发fgc,有时10天或一周左右会有一次。
什么时候执行YGC和FGC
- a.eden空间不足,执行 young gc
- b.old空间不足,perm空间不足,调用方法
System.gc()
,ygc时的悲观策略, dump live的内存信息时(jmap –dump:live),都会执行full gc
5.10各种回收算法
GC最基础的算法有三种:
- 标记 -清除算法
- 复制算法
- 标记-压缩算法
- 我们常用的垃圾回收器一般都采用分代收集算法(其实就是组合上面的算法,不同的区域使用不同的算法)。
具体:
- 标记-清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
- 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
- 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
2.11各种回收器,各自优缺点,重点CMS、G1
图来源于《深入理解Java虚拟机:JVM高级特效与最佳实现》,图中两个收集器之间有连线,说明它们可以配合使用.
- Serial收集器,串行收集器是最古老,最稳定以及效率高的收集器,但可能会产生较长的停顿,只使用一个线程去回收。
- ParNew收集器,ParNew收集器其实就是Serial收集器的多线程版本。
- Parallel收集器,Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。
- Parallel Old收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程“标记-整理”算法
- CMS收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它需要消耗额外的CPU和内存资源,在CPU和内存资源紧张,CPU较少时,会加重系统负担。CMS无法处理浮动垃圾。CMS的“标记-清除”算法,会导致大量空间碎片的产生。
- G1收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。
5.12 stackoverflow错误,permgen space错误
stackoverflow错误主要出现:
- 在虚拟机栈中(线程请求的栈深度大于虚拟机栈锁允许的最大深度)
permgen space错误(针对jdk之前1.7版本):
- 大量加载class文件
- 常量池内存溢出
Java虚拟机(JVM)知多少的更多相关文章
- 深入解析java虚拟机-jvm运行机制
转自oschina 一:JVM基础概念 JVM(Java虚拟机)一种用于计算设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字 ...
- Java虚拟机JVM学习07 类的卸载机制
Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...
- Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论
Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- Java虚拟机JVM学习04 类的初始化
Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...
- Java虚拟机JVM学习03 连接过程:验证、准备、解析
Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- Java虚拟机JVM学习01 流程概述
Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...
- java虚拟机 jvm 出入java栈 栈空间内存分配
java栈空间是一块线程私有的内存空间,java堆和程序数据密切相关,那么java栈就是和线程执行密切相关.线程最基本的执行行为就是函数的调用.每次函数调用其实是通过java栈传递数据的. 数据结构中 ...
随机推荐
- jack语言编译器的实现过程
目录: 1, 背景介绍
- PAT 甲级 1014 Waiting in Line (30 分)(queue的使用,模拟题,有个大坑)
1014 Waiting in Line (30 分) Suppose a bank has N windows open for service. There is a yellow line ...
- Spring Security(3):配置与自动配置的介绍及源码分析
基于注解的配置(Java Configuration)从Spring Security 3.2开始就已经支持,本篇基于Spring boot注解的配置进行讲解,如果需要基于XML配置(Security ...
- iOS-MBProgressHUD框架使用(转)
MBProgressHUD是一个开源类库,实现了各种样式的提示框, 下载地址:https://github.com/jdg/MBProgressHUD,然后把两个MBProgressHUD.h和MBP ...
- WordPress自定义循环
我们在学WordPress的时候,最常用到的就是循环了.写模板的时候,多数的时间都是和循环打交道的.如果你不能很详细的了解WordPress的循环,是很难写出模板来的. 而WordPress自定义循环 ...
- Django之会话机制cookie、session使用
login视图函数: def login(request): if request.method == 'POST': username = request.POST.get('username') ...
- js中函数总结(1)
8.1函数定义js的函数可以嵌套在其他函数的定义中,这样它们就可以访问它们被定义时所处的作用域中的任何变量.意味着js函数构成了一个闭包:function name(){} 特殊的:函数表达式有时定义 ...
- JS获取URL地址
var url = window.location.href;
- 【转载】Jave开发手册之正则表达式预编译
今天又水一篇,java开发手册华山版 一.编程规约 (九)其它 第一条 解释:Pattern要定义为static final静态变量,以避免执行多次预编译. 错误用法: // 没有使用预编译 priv ...
- 洛谷P4779 【模板】单源最短路径
P4779 [模板]单源最短路径(标准版) 题目链接 https://www.luogu.org/problemnew/show/P4779 题目描述 给定一个 N个点,M条有向边的带非负权图,请你计 ...