一个进程就有一个JVM,每个进程之间资源独立

当调用java命令来启动某个Java程序的时候,该命令创建一个独立的进程来运行我们的Java程序。而这个独立的进程里面就包含一个Java虚拟机。不管该程序有多么的复杂,该程序启动了多少个线程,他们都处于该Java对应的进程里。同一个JVM的所有线程,所有变量都在同一个进程里,他们都使用该JVM进程的内存区。
 
当出现以下几种情况的时候,JVM会停止,并释放所有的内存:
(1),程序运行到最后正常结束
(2),程序运行到使用 System.exit(), Runtime.getRuntime().exit()代码处结束程序
(3),程序执行过程中遇到未捕获的异常或错误而结束。
(4),程序所在平台强制结束了JVM进程
 测试代码:
基础类
package com.zmd.jvm;

public class A {
public static int a = 5;
}

多个进行并发运行类

package com.zmd.jvm;

public class Test1 {
public static void main(String[] args) throws InterruptedException {
A.a ++;
System.out.println("A.a" + A.a);
Thread.sleep(10000);
}
}

可以看到Test1同时运行多个进程,每个输出的A.a都是6

***类加载连接和初始化***

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接(验证、准备、解析)、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这3个步骤,所以有时也把这3个步骤统称为类加载或类初始化。
 
一个从使用开始到消亡会经历如下步骤:
0

1,类的加载:

类的加载指的是将类的class文件读入内存,并为之创建java.lang.Class对象。java.lang.Class相当于就是我们类的元数据。
本步骤需要类加载器完成,类加载器通常由JVM 提供,这些类加载器也是前面所程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过ClassLoader基类来创建自己的类加载器。但是开发的时候,千万不要自定义类加载器,就用JVM提供的。
我们使用不同的类加载器可以加载不同来源的.class文件,一般情况下我们有几个来源:
  • 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式
  • 从JAR包加载class文件,这种方式也是很常见的,比如我们使用JDBC时用到的数据库驱动类就放在JAR文件中,JVM 可以从JAR文件中直接加载该class文件
  • 通过网络加载class文件
  • 把Java源文件动态编译,并执行加载
类加载器通常无须等到"首次使用"该类时才加载该类,Java虚拟机规范允许系统预先加载某些类

2,类的连接:

当类被加载之后,系统为之生成1个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE(Java运行时环境)。类连接又可分为如下三个阶段。

(1),验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致,保证类在运行的时候不会有问题。

(2),准备:类准备阶段则负责为类的类变量分配内存,并设置默认初始值。(static int i=5,准备阶段会赋值0.再在初始化阶段赋值5)

(3),解析:将类的二进制数据中的符号引用替换成内存地址指针直接引用

在类的加载过程中的解析阶段,Java虚拟机会把类的二进制数据中的符号引用替换为直接引用,如Worker类中一个方法:
public void gotoWork(){ car.run(); //这段代码在Worker类中的二进制表示为符号引用 }
在Worker类的二进制数据中,包含了一个对Car类的run()方法的符号引用,它由run()方法的全名和相关描述符组成。在解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run()方法在方法区的内存位置,这个指针就是直接引用。

3,类的初始化:

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量(static 变量)进行初始化。Java类中对类变量指定初始值有两种方式:
(1),声明类变量时指定初始值;
(2),使用静态初始化块为类变量指定初始值
比如我们如下程序:
public class Test {
private static int a = 5;
private static int b = 8;
private static int c = 9;
static {
b = 6;
c = 7;
}
public static void main(String[] args) {
System.out.println(a + ", " + b + ", " + c);
}
}
运行结果:5, 6, 7
下面这个初始化块就是在我们程序初始化的时候运行的
static {
  b = 6;
  c = 7;
}
如果我们改动一下程序:
package com.vgxit.jvm;

public class Test {
static {
b = 6;
c = 7;
}
private static int a = 5;
private static int b = 8;
private static int c = 9;
public static void main(String[] args) {
System.out.println(a + ", " + b + ", " + c);
}
}
输出的结果是:5, 8, 9
如果在声明的时候,指定初始值,静态初始化块都将被当成类的初始化语句来执行。这个时候JVM就会从上往下的顺序依次执行。
这个时候有的同学可能就有疑问,上线的代码为什么不报错?
其实我们类的连接的准备阶段,我们就会为类的静态变量分配内存,并设置默认的初始值。而初始化块在运行时是在初始化阶段执行的,在准备阶段之后。这个时候b和c变量已经在内存中了。所以不会报错。

什么时候初始化:

当遇到下面几种情况,就会执行初始化:

(1),创建类的实例(为某个类创建实例的方式包括:使用 new 操作符来创建实例,通过反射来创建实例,通过反序列化的方式来创建实例)
(2), 调用某个类的类方法(静态方法)
(3), 访问某个类或接口的类变量,或为该类变量赋值
(4), 使用反射方式来强制创建某个类或接口对应的 java.lang.C1ass对象。如代码 C1ass.forName("Person"), 如果系统还未初始化Person类,则这行代码将会导致该Person类被初始化。并返回Person类对应的java.lang.C1ass对象。
(5), 初始化某个类的子类。当初始化某个类的子类时,该子类的所有父类都会被初始化
(6), 直接使用 java 命令来运行某个主类
 

例外情况!:

public class ClassInitTest {
private static class TestAAA {
static {
System.out.println("我是初始化块");
}
public static final String text = "V哥好帅";
}
public static void main(String[] args) {
System.out.println(TestAAA.text);
}
}

只输出了“V哥好帅”

为什么?
查看编译后再反编译的main方法的代码:
(IDEA自带反编译功能)
你会惊奇的发现ClassInitTest  main方法中调用的TestAAA.text 类变量,直接被替换成 字符串值

面试答法:

编译优化 : --->

对于类变量,如果该类变量的值在编译的时候就可以确定下来,(如final类型的后面运行中不能更改)。Java编译器在编译的时候,就会产生代码优化,编译器会直接把这个类变量出现的地方全部替换成对应的值(比如上面例子中的“V哥好帅”)。

因此我们上面这个程序从我们程序员写代码的角度看上去使用到了TestAAA的text类变量,但是其实运行时根本就没有用到。所以这个时候就没有发生直观看到被调用类的初始化,所以我们的静态初始化块压根就不会运行。

 如果不是final类型的变量,比如把上面的类变量final去掉

public static  String text = "V哥好帅";

输出为:

我是初始化块
V哥好帅

查看编译结果:

 
 
 
 
 
 
 
 
 

java 理论基础 类的初始化(加载、连接(验证、准备、解析)、初始化)的更多相关文章

  1. 【译】12. Java反射——类的动态加载和重新加载

    原文地址:http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html 博主最近比较忙,争取每周翻译 ...

  2. [改善Java代码]使用forName动态加载类文件

    动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否需要加载一 ...

  3. Java类编译、加载、和执行机制

    Java类编译.加载.和执行机制 标签: java 类加载 类编译 类执行 机制 0.前言 个人认为,对于JVM的理解,主要是两大方面内容: Java类的编译.加载和执行. JVM的内存管理和垃圾回收 ...

  4. JAVA类的静态加载和动态加载以及NoClassDefFoundError和ClassNotFoundException

    我们都知道Java初始化一个类的时候可以用new 操作符来初始化, 也可通过Class.forName()的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance ...

  5. Java类加载机制(加载、验证、准备、解析、初始化)

    如下图所示,Java的类加载机制主要分为三个部分,分别为加载.链接.初始化.其中链接又分为三个小部分--验证.准备.解析. 加载--在经过对Java代码进行编译后,JVM将Java类编译后的二进制文件 ...

  6. java类什么时候加载?,加载类的原理机制是怎么样的?

    java类什么时候加载?,加载原理机制是怎么样的?   答: 很多人都不是很清楚java的class类什么时候加载在运行内存中,其实类加载的时间是发生在一下几种情况: 1.实例化对象时,就像sprin ...

  7. java动态编译类文件并加载到内存中

    如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.cnblogs.com/anai/p/4270 ...

  8. java动态加载类和静态加载类笔记

    JAVA中的静态加载类是编译时刻加载类  动态加载类指的是运行时刻加载类 二者有什么区别呢 举一个例子  现在我创建了一个类  实现的功能假设为通过传入的参数调用具体的类和方法 class offic ...

  9. java reflect 初始学习 动态加载类

    首先要理解Class类: 在java 的反射中,Class.forName("com.lilin.Office") 使用类的全名,这样获取,不仅仅表示了类的类类型,同时还代表着类的 ...

随机推荐

  1. 【Java面试题】-- Java String

    Java String 2019-11-02  17:40:45  by冲冲 1.String的内存位置 String是定义在 java.lang 包下的一个类.它不是基本数据类型.String是不可 ...

  2. javascript-初级-day08

    return <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" ...

  3. 第41篇-JNIEnv与JavaVM的初始化

    JavaVM和JNIEnv的初始化和JVM各模块的初始化都是在JNI_CreateJavaVM()函数中完成.这一篇将详细介绍JavaVM和JNIEnv的初始化过程. 1.初始化JavaVM Java ...

  4. Codeforces 848E - Days of Floral Colours(分治 FFT)

    Codeforces 题目传送门 & 洛谷题目传送门 神仙 D1E,一道货真价实的 *3400 %%%%%%%%%%%% 首先注意到一点,由于该图为中心对称图形,\(1\sim n\) 的染色 ...

  5. 搜索工具Wox简单使用

    目录 下载安装 几个常用命令 自定义 Wox是快速搜索小工具,内置了everything(需要先安装),但比everything好用.不止是搜文件,网页.系统等都可以快速搜索,还可以自定义. 下载安装 ...

  6. 43-Reverse Nodes in k-Group

    Reverse Nodes in k-Group My Submissions QuestionEditorial Solution Total Accepted: 58690 Total Submi ...

  7. Telink BLE MESH PWM波的小结

    本本针对Telink BLE MESH SDK  灯控的使用进行说明. 1.调整灯光的频率 默认情况下 SDK PWM波的频率是 600HZ的,有时我们需要将它调整频率,例如调整为4K,只需要更改参数 ...

  8. 分布式事务(4)---最终一致性方案之TCC

    分布式事务(1)-理论基础 分布式事务(2)---强一致性分布式事务解决方案 分布式事务(3)---强一致性分布式事务Atomikos实战 强一致性分布式事务解决方案要求参与事务的各个节点的数据时刻保 ...

  9. Hadoop fs.copyToLocalFile错误

    fs.copyToLocalFile(new Path("/study1/1.txt"), new Path("C:/Users/Administrator/Deskto ...

  10. CSS系列,清除浮动方法总结

    在非IE浏览器(如Firefox)下,当容器的高度为auto,且容器的内容中有浮动(float为left或right)的元素.在这种情况下,容器的高度不能自动伸长以适应内容的高度,使得内容溢出到容器外 ...