JVM 心得分享(加载 链接 初始化)

所属分类: 软件编程 / java 阅读数: 20
收藏 0 赞 0 分享

基本概念:类加载的过程大致分为三个阶段

1、加载阶段:本阶段主要把class的二进制代码加载进入JVM,并且进行常量池(类名,方法名,字段名),方法区(二进制字节码),栈(本地方法栈结构),堆(java.lang.class对象)的设置。

有三个加载类:Bootstrap ClassLoader,加载jre/lib/下的类;

Extension ClassLoader:加载jre/lib/ext下的类;

ApplicationClassLoader:加载classpath下的类(应用程序自己开发的类,如 工程目录/bin/下的.class文件)

还有一个扩展的加载类,满足应用程序的特殊需求。类的加载时,父亲loader优先执行load动作,父亲load不了时,子类运作。

2、链接阶段:又分为三个小阶段 校验,准备,解析。

校验:实施字节码文件的格式,语法等的校验。

准备:对静态变量申请存储空间,并设置默认的初始值。如:private static int a =2;那么在准备阶段a被设置为0;

解析:把方法区中的符号指针替换为直接引用。

3、初始化阶段:对静态变量进行初始化,执行静态块,创建类的实例。上述的a变量在初始化阶段会被设置为2。

第一步:验证静态变量和静态块的加载+链接(校验,准备,解析)+初始化过程中值的变化。

 package com.chong.studyparalell.clazz.loader;

public class ClassLoaderDemo {
public static void main(String []args){
Test test2 = new Test();

System.out.println("Test2实例化结束"+test2.toString());

}
}
package com.chong.studyparalell.clazz.loader;

public class Test{

private static Test test1 = new Test();
private static int a = 2;
private static int b = 2;

static {
System.out.println("【Test类静态块】a=" + a);
}

public Test(){

System.out.println("【Test类构造方法】a=" + a);
System.out.println("【Test类构造方法】b=" + b);
System.out.println("【Test类实例】" + this.toString());
}

public static Test newInstance(){
return test1;
}
}

log输出如下:

1 【Test类构造方法】a=0
2 【Test类构造方法】b=0
3 【Test类实例】com.chong.studyparalell.clazz.loader.Test@16c1857
4 【Test类静态块】a=2
5 【Test类构造方法】a=2
6 【Test类构造方法】b=2
7 【Test类实例】com.chong.studyparalell.clazz.loader.Test@1b1fd9c
8 Test2实例化结束com.chong.studyparalell.clazz.loader.Test@1b1fd9c

首先Test类在链接阶段(准备阶段),a,b分别被设置默认值0。

当new Test()执行后,

1)首先初始化Test类的三个静态变量 test1,a,b。

初始化test1时,第一次调用构造方法,此时a,b为0。对应日志1,2行。

实例化test1,日志第3行。

test1初始化完成后,继续初始化a,b,设为2。

接着初始化静态块 ,对应日志第4行。

2)执行Test类的构造方法

因为a,b已经被初始化为2,所以执行类的构造方法时,会输出a,b 为2。日志第5,6行。

实例化后输出地址信息,日志第7行。

3)最终main方法里打出实例工作完成,日志第8行。

第二步,加入父类后,进行确认。

package com.chong.studyparalell.clazz.loader;

public class TestBase {

private static int base_a = 2;
private static int base_b = 2;

static {
System.out.println("【父类静态块】 base_a="+base_a);
}

public TestBase(){
System.out.println("【父类 构造方法】base_a=" + base_a);
System.out.println("【父类 构造方法】base_b=" + base_b);
System.out.println("【父类 实例】"+ this.toString());
}

}
package com.chong.studyparalell.clazz.loader;

public class Test extends TestBase{
内容同第一步;
}

log输出如下:

【父类静态块】 base_a=2

【父类 构造方法】base_a=2

【父类 构造方法】base_b=2

【父类 实例】com.chong.studyparalell.clazz.loader.Test@19ab8d

【Test类构造方法】a=0

【Test类构造方法】b=0

【Test类实例】com.chong.studyparalell.clazz.loader.Test@19ab8d

【Test类静态块】a=2

【父类 构造方法】base_a=2

【父类 构造方法】base_b=2

【父类 实例】com.chong.studyparalell.clazz.loader.Test@14dcfad

【Test类构造方法】a=2

【Test类构造方法】b=2

【Test类实例】com.chong.studyparalell.clazz.loader.Test@14dcfad

Test2实例化结束com.chong.studyparalell.clazz.loader.Test@14dcfad

可以发现父类的静态变量,静态块,构造方法首先被初始化。然后子类的静态变量,静态块和构造方法被初始化。

第三步:写一个自定义的类加载器

 package com.chong.studyparalell.clazz.loader;


public class MyClassLoaderPilot {

public static void main(String[] args) {

try {
MyClassLoader classLoader = new MyClassLoader();
String filename = "com.chong.studyparalell.demon.DemonThreadDemo";

Object clazz = classLoader.loadCustomizeClass(filename);
System.out.println(clazz);

} catch (Exception e) {
e.printStackTrace();
}
}

}

package com.chong.studyparalell.clazz.loader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {

private String demoPath = "D:\\work\\temp\\";

public Class<?> loadCustomizeClass(String filename) throws ClassNotFoundException {

FileInputStream fis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// 1.获取class文件的字节码(二进制数据)
String[] fileNames = filename.split("\\.");
fis = new FileInputStream(demoPath + fileNames[fileNames.length-1] +".class");
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
baos.write(bytes, 0, len);
}

} catch (Exception e) {
throw new ClassNotFoundException();
} finally {
try {
fis.close();
} catch (IOException e) {
throw new ClassNotFoundException();
}
}

byte[] paramArrayOfByte = baos.toByteArray();
// 2。把二进制文件定义为class对象返回
return defineClass(filename, paramArrayOfByte, 0, paramArrayOfByte.length);
}
}

日志输出如下:

class com.chong.studyparalell.demon.DemonThreadDemo

实际的跟着代码走一遍,看看控制台的输出,用自己的思路虚拟着跟一跟,对于类的加载过程能够认识的更加清晰一些。

以上这篇JVM 心得分享(加载 链接 初始化)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多