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,一经查实,立即删除!

相关文章

什么是mvc?

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

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

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

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 face_recognize_python2.7使用face_recognition做人脸识别

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

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…

opengl es的射线拾取

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

视觉学习(4) —— 添加地址传递数据

Modbus Slave 选择一个地址右键,选择发送的数据类型 视觉软件 一、添加地址 当地址为100时,先将首地址改为100,第0个地址为100,第1个地址为101,往后累加 若想使用100—150的地址,即首地址为100&#xff…

jquery中阻止事件冒泡的方法

2019独角兽企业重金招聘Python工程师标准>>> 根据《jquery基础教程》 第一种方法:判断事件的“直接”目标是否是自身,如果不是自身,不予处理 $(div.outter).click(function(event) {if (event.target this) {$(p).css(color, red…

java swing 组织机构_课内资源 - 基于Java Swing的小型社团成员管理系统

一、需求分析1.1 个人信息学号、姓名、性别、年级、系别、专业、出生日期、联系方式、个性签名、地址、照片。1.2 基本功能要求管理员信息管理登录、注销功能修改密码功能部落成员信息管理添加成员删除成员修改成员信息按条件查找筛选成员1.3 高级特性管理员权限管理成员信息包…

centos下安装pip时失败:

2019独角兽企业重金招聘Python工程师标准>>> [rootwfm ~]# yum -y install pip Loaded plugins: fastestmirror, refresh-packagekit, security Loading mirror speeds from cached hostfile * base: mirrors.tuna.tsinghua.edu.cn * extras: mirrors.tuna.tsinghua…

java写手机游戏_如何将自己编写的JAVA小游戏写到手机里?

2019-06-19怎么用java编写获取星期几的程序&#xff1f;import java。util。*; public class WeekDay { Calendar date Calendar。getInstance(); private int getMaxDate(int moth){ moth moth -1; if(moth > 12 || moth < 0){ System。 out。println("输入月份错…

gitlab修改默认端口

部署gitlab的时候&#xff0c;一启动&#xff0c;发现80和8080端口已经被占用&#xff0c;无奈&#xff0c;只得先将监听80端口的nginx和监听8080端口的jenkins停止。这会儿有空&#xff0c;琢磨一下如何修改gitlab的默认端口。 修改主要分为两部分&#xff0c;一部分是gitlab总…

深入理解Netscaler INat

深入理解Netscaler INatNetscaler的INat主要是用作基于目的地址的转换&#xff0c;将client访问的公网IP通过Netscaler转换成服务器的私网IP&#xff0c;与DNAT作用类似。由于Netscaler默认的工作机制就是同时做源IP&#xff1a;【源端口】目的IP&#xff1a;【目的端口】的转换…

为什么玩我的世界老提示Java se错误_我的世界error错误信息 error could解决方法

我的世界是一个及其开放的沙盒游戏&#xff0c;而在这个游戏中有不少的问题&#xff0c;比如说遇到error该如何解决呢&#xff0c;看小编给大家带来的我的世界error错误的解决方法&#xff0c;希望大家喜欢。error应用程序错误信息。包括“Error:Unable to access jarfile mcpc…

javascript OOP(下)(九)

一、javascript模拟重载 java中根据参数类型和数量的区别来实现重载&#xff0c;javascript弱类型&#xff0c;没有直接的机制实现重载&#xff0c;javascript中参数类型不确定和参数个数任意&#xff0c;通过判断实际传入的参数的个数来实现重载。 <script> function Pe…

Linux如何查找大文件或目录总结

转载&#xff1a;http://www.cnblogs.com/kerrycode/p/4391859.html 在Windows系统中&#xff0c;我们可以使用TreeSize工具查找一些大文件或文件夹&#xff0c;非常的方便高效&#xff0c;在Linux系统中&#xff0c;如何去搜索一些比较大的文件呢&#xff1f;下面我整理了一下…

java需要会的工具_Java开发者必备的几款工具,一定要掌握!

原标题&#xff1a;Java开发者必备的几款工具&#xff0c;一定要掌握&#xff01;NotepadNotepad是用于编辑xml、脚本以及记笔记的最佳工具。这个工具的最好部分在于&#xff0c;你在Notepad上打开的任何一个文档&#xff0c;在关闭后都会有一个残留文档&#xff0c;它有助于在…

Android推荐的几本书

2019独角兽企业重金招聘Python工程师标准>>> 第一阶段 <<第一行代码Android>><<疯狂Android>>第二阶段 <<Android开发艺术探索>><<Android群英传>>Android源码 第三阶段 <<Android开发艺术探索>><&…

tdr上升时间什么设定_TDR的完整形式是什么?

tdr上升时间什么设定TDR&#xff1a;时域反射仪/车票寄存收据/定期存款收据 (TDR: Time Domain Reflectometer/ Ticket Deposit Receipt/ Term Deposit Receipt) 1)TDR&#xff1a;时域反射仪 (1) TDR: Time Domain Reflectometer) TDR is an abbreviation of the "Time D…