java双缓存机制_详解JVM类加载机制及类缓存问题的处理方法

前言

大家应该都知道,当一个Java项目启动的时候,JVM会找到main方法,根据对象之间的调用来对class文件和所引用的jar包中的class文件进行加载(其步骤分为加载、验证、准备、解析、初始化、使用和卸载),方法区中开辟内存来存储类的运行时数据结构(包括静态变量、静态方法、常量池、类结构等),同时在堆中生成相应的Class对象指向方法区中对应的类运行时数据结构。

用最简单的一句话来概括,类加载的过程就是JVM根据所需的class文件的路径,通过IO流的方式来读取class字节码文件,并通过一系列解析初始化等步骤来注入到内存。 java中的类加载器有:BootstrapClassLoader(最上层)、ExtClassLoader、AppClassLoader、以及用户自定义的ClassLoader(最下层)。JVM对于不同种类的jar包(或class文件),会有不同种类的类加载器进行加载。

对应关系如下:

BootstrapClassLoader  用于加载JVM运行所需要的类:

JAVA_HOME/jre/lib/resources.jar:

JAVA_HOME/jre/lib/rt.jar:

JAVA_HOME/jre/lib/sunrsasign.jar:

JAVA_HOME/jre/lib/jsse.jar:

JAVA_HOME/jre/lib/jce.jar:

JAVA_HOME/jre/lib/charsets.jar:

JAVA_HOME/jre/lib/jfr.jar:

JAVA_HOME/jre/classes

ExtClassLoader 用于加载扩展类:

../Java/Extensions:

../JAVA_HOME/jre/lib/ext:    ../Library/Java/Extensions:/Network/Library/Java/Extensions:

../System/Library/Java/Extensions:

../lib/java

AppClassLoader 用于加载我们项目中ClassPath下所创建的类和jar包中引用的类。

整个类加载,是通过一种叫做双亲委派的机制来进行加载。

举例来说,一个类被最下层的加载器(用户自定义ClassLoader)进行加载,此加载器首先会调用上一层的加载器(AppClassLoader)进行加载,而AppClassLoader会继续转交给上层(ExtClassLoader)的加载器进行加载,直到BootstrapClassLoader。  如果BootstrapClassLoader所加载的类路径找不到此类,那么才会交给下一层的加载器(ExtClassLoader)进行加载,如果找不到此类,继续交给下一层(AppClassLoader)进行加载。以此类推,如果用户自定义的ClassLoader也找不到此类,那么程序就会抛出一个ClassNotFoundError。

整个加载过程图示如下:

c752b3d6e2e41a434ba905665b989efb.png

(图片引用自:https://www.cnblogs.com/xing901022/p/4574961.html)

类加载源的源码跟踪如下(在此对源码进行了适当的简化),读者可以点入源码进行查看:

package java.lang.ClassLoader;

import ....

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// First,在虚拟机内存中查找是否已经加载过此类...类缓存的主要问题所在!!!

Class> c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

if (parent != null) {

//先让上一层加载器进行加载

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

if (c == null) {

//调用此类加载器所实现的findClass方法进行加载

c = findClass(name);

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

在源码中可以完全领略到双亲委派机制的过程,其中最重要的三句代码已经进行了标注:

findLoadedClass(在虚拟机内存中查找是否已经加载过此类...类缓存的主要问题所在!!!)

parent.loadClass(先让上一层加载器进行加载)

findClass(调用此类加载器所实现的findClass方法进行加载)

如果用户需要自定义加载器,加载自己指定路径的class文件,需要继承ClassLoader,并实现findClass(String name)方法。举例如下:

package com.linuxidc.utils;

import java.io.ByteArrayOutputStream;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

public class ServiceClassLoader extends ClassLoader{

private String classPath;

public ServiceClassLoader(String classPath) {

this.classPath = classPath;

}

/**

* 重写父类的findClass 方法。 父类的loadClass会调用此方法

*/

@Override

protected Class> findClass(String name) throws ClassNotFoundException {

Class> c = null;

byte[] classData = getClassData(name);

if (classData!=null) {

c = defineClass(name, classData, 0, classData.length);

}else {

throw new ClassNotFoundException();

}

return c;

}

// 将class文件通过IO流读取,转化为字节数组

private byte[] getClassData(String name) {

String path = classPath + "/"+ name.replace('.', '/') + ".class";

InputStream iStream = null;

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

try {

iStream = new FileInputStream(path);

byte[] buffer = new byte[1024];

int temp = 0;

while ((temp = iStream.read(buffer))!=-1) {

byteArrayOutputStream.write(buffer, 0, temp);

}

if (byteArrayOutputStream!=null) {

return byteArrayOutputStream.toByteArray();

}

} catch (Exception e) {

e.printStackTrace();

}finally {

try {

if (iStream!=null) {

iStream.close();

}

} catch (IOException e) {

e.printStackTrace();

}

try {

if (byteArrayOutputStream!=null) {

byteArrayOutputStream.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

return null;

}

}

对类加载器的使用代码如下:

ServiceClassLoader serviceClassLoader = new ServiceClassLoader("c:\myclass");

Czlass> c = ServiceClassLoader.loadClass("com.linuxidc.service.Myclass");

如果 用同一个 ServiceClassLoader 对象去加载同一个Class文件多次,每次加载后的Class对象为同一个! 然而如果new不同的自定义ClassLoader去加载同一个Class文件,则每次会返回不同的Class对象。

注意: 不能将所要加载的Class文件放到classpath目录及其任何子目录下,否则会被AppClassLoader优先加载 (这是由于类加载采用双亲委派机制,同时AppClassLoader可以加载所有在classpath下的class文件), 每次都是同一个AppClassLoader进行加载,因此会出现类缓存问题。

这样就解决了通常在JVM类加载时,直接使用反射出现的类缓存的问题。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/541913.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle中dbms_并发和由于DBMS中的并发导致的问题

oracle中dbms并发 (Concurrency) The ability of a database system which handles simultaneously or a number of transactions by interleaving parts of the actions or the overlapping this is called concurrency of the system. 数据库系统通过交织部分操作或重叠操作来…

什么是mvc?

什么是MVCMVC 是一种设计模式,它将应用划分为3 个部分:数据(模型)、展现层(视图)和用户交互层(控制器)。换句话说,一个事件的发生是这样的过程:1.…

mysql的安装和基本命令_MySQL安装以及简单命令用法

MYSQL:关系型数据库存储引擎:负责将逻辑层的概念转化为物理层机制,在物理层完成物理机制。支持事务:transaction必须满足的条件:ACID(一致性,持久性,原子性,隔离性)锁:并发访问随机访问:数据在磁盘上是随机存储的安装&…

将数组转换为JavaScript中的对象

Lets say you have the following array, 假设您有以下数组, const nums [1, 2, 3, 4, 5];console.log(nums);Output 输出量 (5) [1, 2, 3, 4, 5]We know that nums is an array and we can see in the output that we get an array. Lets convert it into an ob…

docker集群运行在calico网络上

2019独角兽企业重金招聘Python工程师标准>>> ##网络及版本信息 docker1 centos7 192.168.75.200 docker2 centos7 192.168.75.201 物理网络 192.168.75.1/24 Docker version 1.10.3, build 3999ccb-unsupported ,安装过程略 # calicoctl version Version…

python批量雷达图_python批量制作雷达图

老板要画雷达图,但是数据好多组怎么办?不能一个一个点excel去画吧,那么可以利用python进行批量制作,得到样式如下:首先制作一个演示的excel,评分为excel随机数生成:1 INT((RAND()4)*10)/10加入标…

JavaScript中带有示例的Math.log()方法

JavaScript | Math.log()方法 (JavaScript | Math.log() Method) Math.log() is a function in math library of JavaScript that is used to return the value of natural Log i.e. (base e) of the given number. It is also known as ln(x) in mathematical terms. Math.log…

SUI踩坑记录

SUI踩坑记录 最近做了个项目选型了SUI和vue做单页应用。下面记录一下踩坑经历SUI 介绍 sui文档:http://m.sui.taobao.org/SUI Mobile 是一套基于 Framework7 开发的UI库。它非常轻量、精美,只需要引入我们的CDN文件就可以使用,并且能兼容到 i…

java 写入xml文件_java读写xml文件

要读的xml文件李华姓名>14年龄>学生>张三姓名>16年龄>学生>学生花名册>package xml;import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.io.Writer;import java.util.Iterator;import java.util.Vector;import javax.xml.pa…

JavaScript中带有示例的Math.max()方法

JavaScript | Math.max()方法 (JavaScript | Math.max() Method) Math.max() is a function in math library of JavaScript that is used to return the greatest value of all the passed values to the method. Math.max()是JavaScript数学库中的函数,用于将所有…

java 修饰符默认_Java和C#默认访问修饰符

C#中:针对下面几种类型内部成员的访问修饰符:enum的默认访问修饰符:public。class的默认为private。interface默认为public。struct默认为private。其中:public可以被任意存取;protected只可以被本类和其继承子类存取&…

JavaScript中带有示例的Math.abs()方法

JavaScript | Math.abs()方法 (JavaScript | Math.abs() Method) Math operations in JavaScript are handled using functions of math library in JavaScript. In this tutorial on Math.abs() method, we will learn about the abs() method and its working with examples.…

人脸识别python face_recognize_python2.7使用face_recognition做人脸识别

偶然看到一篇文章,说是可以实时人脸识别,很有兴趣就自己按照文章开始动手人脸识别,但是实现过程中遇到了几个问题这里做个总结,希望可以帮助到大家安装face_recognition这个之前需要先安装编译dlib,如果没有安装dlib&a…

c# reverse_清单 .Reverse()方法,以C#为例

c# reverseC&#xff03;List <T> .Reverse()方法 (C# List<T>.Reverse() Method) List<T>.Reverse() method is used to reverse the all list elements. List <T> .Reverse()方法用于反转所有列表元素。 Syntax: 句法&#xff1a; void List<T&…

cpuinfo详解

cat /proc/cpuinfo processor: 23&#xff1a;超线程技术的虚拟逻辑核第24个 ###一般看最后一个0...23 表示24线程 vendor_id: GenuineIntel&#xff1a;CPU制造商cpu family: 6&#xff1a;CPU产品系列代号model: 44&#xff1a;CPU属于其系列中的哪一代号model name: Intel…

jvm延迟偏向_用于偏向硬币翻转模拟的Python程序

jvm延迟偏向Here, we will be simulating the occurrence coin face i.e. H - HEAD, T - TAIL. Simply we are going to use an inbuilt library called as random to call a random value from given set and thereby we can stimulate the occurrence value by storing the o…

java项目没有bin_WebAPI项目似乎没有将转换后的web.config发布到bin文件夹?

我很擅长.NET配置转换 . 我现在将它们放在用于数据使用的类库和WPF应用程序上 .但是&#xff0c;当我尝试使用ASP.NET WebAPI项目进行设置时&#xff0c;似乎发生了一些奇怪的事情 .配置文件永远不会显示在我的bin目录中&#xff0c;因此web.config始终显示为预先形成的配置文件…

opengl es的射线拾取

2019独角兽企业重金招聘Python工程师标准>>> 在opengl中关于拾取有封装好的选择模式&#xff0c;名字栈&#xff0c;命中记录&#xff0c;实现拾取的功能&#xff0c;相对容易一些。但是到了opengl es里面就比较倒霉了&#xff0c;因为opengl es是opengl的简化版&am…

java timezone_Java TimeZone useDaylightTime()方法与示例

java timezoneTimeZone类useDaylightTime()方法 (TimeZone Class useDaylightTime() method) useDaylightTime() method is available in java.util package. useDaylightTime()方法在java.util包中可用。 useDaylightTime() method is used to check whether this time zone u…