谈谈java虚拟机
本文可作为北京圣思元深入java虚拟机的课堂笔记。
先看一个令人dan teng的面试题
public class Singleton { public static Singleton s=new Singleton(); public static int k1; public static int k2=0; private Singleton(){ k1++; k2++; } public static void main(String[] args) { System.out.println(Singleton.k1); System.out.println(Singleton.k2); } }
请问输出什么?
如果是下面这样呢?
public class Singleton { public static int k1; public static int k2=0; public static Singleton s=new Singleton(); private Singleton(){ k1++; k2++; } public static void main(String[] args) { System.out.println(Singleton.k1); System.out.println(Singleton.k2); } }
诸位看官莫急,咱们慢慢来从虚拟机的层次来看这个问题。
什么是虚拟机?虚拟机是干什么的?
这些资料大家百度,我就不赘述了。
虚拟机的生命周期
在以下几种情况下,java虚拟机将结束生命周期。
1 执行了System.exit()方法
通过查看api文档,我们exit的参数为int,当参数为0就是正常结束,否则就是非正常结束。
2 程序正常结束
3 程序执行过程中遇到异常或错误
4 操作系统出现错误
类的加载,连接与初始化
大体的图如下
加载: 查找并加载类的二进制数据
连接:
-验证 确保被加载的类的正确性
-准备 为类的静态变量分配内存,并将其初始化为默认值(int就是0 boolean就是 false 引用类型就是null)
-解析 把类中的符号引用变为直接引用
初始化: 为类的静态变量赋予正确的初始值
不知道诸位看官是否有什么疑问?
如果大家没有,我倒是有个问题,在我们最开始编写java的时候总是javac命令编译成class字节码,java命令运行。如果java代码有什么问题,在javac的时候就会抛出问题,换句话说等我们连接class文件的时候它肯定是没问题的,那还验证什么呀?
答案就是:
如果class的产生只能通过javac命令的话,那就没有任何问题了,可关键就是人们也可以手动产生class文件,所以验证这一步还是有用的。
第二个问题 在连接的准备阶段有把静态变量初始为默认值,但是在初始化的阶段也有为类的静态变量赋予正确的初始值。这两个步骤有什么关系?
看如下代码片
public class test{ public static int a=5; public static int b; public static int c; static{ c=10; } public static Person p; }
在准备阶段,a与b的值都是0(因为int型默认都是0) p为null(Person 就是简单的一个类)
到初始化阶段,a就是5了,b仍然是0,c是10,p还是null。
换句话说,只有在初始化阶段,赋值的=才发挥作用。(对a的赋值与下面的static代码块是一回事)
(准备阶段与初始化阶段对静态变量的赋值顺序,都是按照变量的书写顺序来的。上面的例子就是先给a再给b,然后c,最后p)
一步一步来
加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
如下图
类的加载的最终产品是位于堆区中的Class对象
这里就有另一个名词了Class对象。
就像每一本书有作者,价格等描述自身一些熟悉的信息,每个类也都有一个描述自己信息的东西,它就是class对象。
例如
public class Person{
public String name;
public String getName(){
return name;
}
}
这个类的Class对象里就包含了这个类里面有几个属性,每个属性是什么类型,有什么方法,每个方法的参数都是什么,返回值都是什么等等。
具体信息,参考api。
谈到类的加载,就得说说类的加载器了。
java中有两种类的加载器
– Java虚拟机自带的加载器
根类加载器(Bootstrap)
(用c++实现 大家都看不到)
扩展类加载器(Extension)
系统类加载器(System)
(扩展类加载器与系统类加载器是用java写的)
-用户自定义的类加载器
java.lang.ClassLoader的子类
用户可以定制类的加载方式
看如下代码片
Class c1=Class.forName("java.lang.String"); Class c2=Class.forName("Singleton"); //就是上面的那个Singleton System.out.println(c1.getClassLoader()); System.out.println(c2.getClassLoader());
运行结果就是
null
sun.misc.Launcher$AppClassLoader@fb56b1
String是Bootstrap加载的,用c++写的我们看不到,所以是null
AppClassLoader就是系统加载器
连接之验证
类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。
如下图
连接之准备
上文已经解释。
连接之解析
如下图
至于什么是符号引用,大家在eclipse中,打开一个class文件
上面红框里面的就是符号引用
初始化
Java程序对类的使用方式可分为两种
主动使用
被动使用
所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们。
主动使用包含以下六种情况
创建类的实例 (如在代码中new Person())
访问某个类或接口的静态变量,或者对该静态变量赋值 (Singlean.a=8)
调用类的静态方法 (Singleton.getInstance();)
反射(如Class.forName(“com.shengsiyuan.Test”)
初始化一个类的子类 (有father类,有child类,且child继承或实现father类。 )
Java虚拟机启动时被标明为启动类的类(调用java启动命令 如Java Test)
除了以上六种,别的调用方法都是被动使用。
再说说最开始的那个题目
public static Singleton s=new Singleton(); public static int k1; public static int k2=0; private Singleton(){ k1++; k2++; }
在连接的准备阶段 k1,k2都是0,s是null
到了初始化阶段, s的初始化调用了构造函数
k1,k2都从0变成了1
继续往下阅读
public static int k1; public static int k2=0;
k1没有被赋值还是1
但是k2却被赋值为0了
所以最后的结果就是
1
0
至于把
public static Singleton s=new Singleton();
放到后面,大家自己分析吧.
谈谈java虚拟机的更多相关文章
- 《深入理解Java虚拟机》读书笔记2--垃圾回收
回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析 转载:http://blog.csdn.net/tjiyu/article/details/5398 ...
- java基础(一):谈谈java内存管理与垃圾回收机制
看了很多java内存管理的文章或者博客,写的要么笼统,要么划分的不正确,且很多文章都千篇一律.例如部分地方将jvm笼统的分为堆.栈.程序计数器,这么分太过于笼统,无法清晰的阐述java的内存管理模型: ...
- Java虚拟机垃圾回收:基础点(转载)
1.Java虚拟机垃圾回收 垃圾回收,或称垃圾收集(Garbage Collection,GC)是指自动管理回收不再被引用的内存数据. 在1960年诞生于MIT的Lisp语言首次使用了动态内存分配和垃 ...
- 《Java虚拟机原理图解》 1.2.3、Class文件里的常量池具体解释(下)
NO9.类中引用到的field字段在常量池中是如何描写叙述的?(CONSTANT_Fieldref_info, CONSTANT_Name_Type_info) 一般而言.我们在定义类的过程中会定义一 ...
- 每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
- 浅谈java虚拟机|系列1|架构简介
今天开了一个专题.谈谈我们java程序员每天面对的java虚拟机(jvm). 本质上来说,jvm分两部分:编译器(compiler)和运行时(runtime). 所谓的编译器,简单来说,他就是个翻译机 ...
- 《深入理解Java虚拟机》第2版挖的坑终于在第3版中被R大填平了
这是why技术的第34篇原创文章 本周还是在家办公的一周,上面的图就是我在家的工位,和上周<Dubbo Cluster集群那点你不知道的事>这篇文章里面的第一张图片比起来,升级了显示器支撑 ...
- 深入Java虚拟机--判断对象存活状态
程序计数器,虚拟机栈和本地方法栈 首先我们先来看下垃圾回收中不会管理到的内存区域,在Java虚拟机的运行时数据区我们可以看到,程序计数器,虚拟机栈,本地方法栈这三个地方是比较特别的.这个三个部分的特点 ...
- 【深入Java虚拟机】之四:类加载机制
类加载过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 其中类加载的过程包括了加载.验 ...
随机推荐
- 初识Java多线程编程
Java 多线程编程 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别 ...
- FORM开发之键性弹性域开发
1.创建表时带有键弹性域字段 SUMMARY_FLAG VARCHAR2(1) , /* 必须有此字段 */ ENABLED_FLAG VARCHAR2(1) , /* 必须有此字段 */ START ...
- Android Studio中Git的配置及协同开发
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51595096 本文出自:[openXu的博客] 目录: 一 Android Stutio配置 ...
- JAVA之旅(三十三)——TCP传输,互相(伤害)传输,复制文件,上传图片,多并发上传,多并发登录
JAVA之旅(三十三)--TCP传输,互相(伤害)传输,复制文件,上传图片,多并发上传,多并发登录 我们继续网络编程 一.TCP 说完UDP,我们就来说下我们应该重点掌握的TCP了 TCP传输 Soc ...
- UNIX环境高级编程——单实例的守护进程
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h&g ...
- DFS(深度优先)算法编程实践
DFS定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种.是一种在开发爬虫早期使用较多的方法.它的目的是要达到被搜索结构的叶结点 . 特点 每次深度优先搜索的结果必 ...
- Java基础----Java---集合框架---泛型、泛型方法、静态方法泛型、泛型接口、泛型限定、泛型类
泛型:jdk1.5后的新特性,用于解决安全问题,是一个安全机制. 好处: 1.将运行时的异常出现问题classcastException.转移到了编译时期.方便程序员调试解决问题,让运行事情问题减少, ...
- there was no endpoint listening at net.pipe://localhost/PreviewProcessingService/ReportProcessing
当你在开发reporting service报表时,进行报表的preview时报下图中的错误,以下方法可以让你直接跳过这个错误,继续查看报表的运行结果. 直接选择你需要运行查看的报表右击run就可以, ...
- Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.
废了半天劲才解决... 就三步:菜单栏,Tools -> Adnroid -> enable ADB integration
- Android初级教程理论知识(第八章网络编程二)
HttpClient 发送get请求 创建一个客户端对象 HttpClient client = new DefaultHttpClient(); 创建一个get请求对象 HttpGet hg = n ...