一个jvm中的类只能加载一次?

在网上看到有些人说“一个类在一个jvm中只能加载一次”,对此产生了怀疑。

另外,在用flink、spark做计算的时候,有一个疑惑,如果用相同的jar包启动了相同的任务,而这两个任务被分配到了同一个进程的不同线程里,是不是意味着这两个任务是共用类的?如果是这样,写flink程序的时候,操作类成员变量岂不是成为一种很危险的动作?

 

然后我就尝试去研究了一下flink的源码,只看到了一小部分,结合一些实验+一些大胆猜测得出结论如下:一个类在一个jvm里可以加载多次,而flink的两个任务不会共用相同的类(即便他们是来自相同jar包的同名类)。

 

得出这个结论只需要了解java的类加载机制。java中有四种类加载器,程序运行需要的类都会由他们加载到内存中。这四种种类加载器分别是BootStrap ClassLoader、Extension ClassLoader、Application ClassLoader、自定义ClassLoader。

一个类由它的全路径限定符和加载它的classLoader决定,也就是说同一个jar中的class如果被不同的类加载器进行了加载,那么这个类在内存就会存在两份,而且两个类并不相同。

看如下例子:(参考自https://www.iteye.com/blog/yongxin10020071209191159-252393

待加载类:

import java.util.Random;  
  
  public class IntProducer  
  {  
        //用随机值来加载静态域赋值,如果被加载两次age应该会是不同的值  
    public static int age = getRandom();  
  
    public static int getRandom()  
    {  
        Random ran = new Random(System.nanoTime());  
        int rand = ran.nextInt(10);  
        System.out.println("IntProducer 被加载,产生随机数:"+rand);  
        return rand;  
  
    }  
  
 }  

加载测试:

import java.net.URL;
import java.net.URLClassLoader;

public class Main {
    public static void main(String[] args) throws Exception
    {
        System.out.println(Main.class.getClassLoader());


        URL[] us = { new URL("file:///Users/zgy/test/") };

        ClassLoader loader1 = new URLClassLoader(us);
        Class c1 = loader1.loadClass("IntProducer");

        System.out.println(c1.getClassLoader()+":"+c1.getField("age").getInt(c1));

        //再加载一次
        ClassLoader loader2 = new URLClassLoader(us);
        Class c2 = loader2.loadClass("IntProducer");
        System.out.println(c2.getClassLoader()+":"+c2.getField("age").getInt(c2));

    }
}

输出如下:

sun.misc.Launcher$AppClassLoader@18b4aac2
IntProducer 被加载,产生随机数:1
java.net.URLClassLoader@61bbe9ba:1
IntProducer 被加载,产生随机数:0
java.net.URLClassLoader@1d44bcfa:0

可以看到同一个类被加载了两次,对应的classLoader是不一样的。

根据flink的任务提交代码,可以看到在任务分发的时候,同时将classLoader也发送了出去,可以推测出,在任务运行的线程中会恢复这个classLoader,并使用此classLoader加载需要的类。

 

然后,转折来了。标题里的说法就是错的吗?也不全是,只是需要加上限定条件。在不指定ClassLoader的时候,程序两次加载同一个类,确实只是会加载一次,这个是完全必要的,因为只有这样才能保证类加载的安全。否则,想用到一个类,加载两次反而是不一样的,岂不是疯了。jvm专门设计了双亲委派模型,来保证这种加载机制。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java通常在程序启动时就被加载,而且在运行过程一般不会重新加载。但是,有些情况下可能需要重新加载,比如在开发过程修改了的源代码后需要重新加载以测试修改是否生效。 为了重新加载,可以使用Java的热部署(HotSwap)技术,该技术可以在不停止程序的情况下重新加载已经被加载。在Java,可以通过使用一些工具或框架来实现热部署,比如JRebel和Spring Loaded等。 除了热部署之外,还可以通过使用Java类加载器来重新加载Java有多个类加载器,可以通过调用类加载器的reload方法来重新加载已经被加载。但是,这种方法比较复杂,需要程序员自己实现。因此,在实际开发,一般更倾向于使用热部署技术来重新加载。 ### 回答2: 在Java的重新加载是指在运行时动态地改变或重新加载一个已经加载过的Java虚拟机的类加载机制规定,每个在内存只能加载一次,即使重新加载一个,也不会替换掉原来已经加载。 尽管如此,我们可以通过一些技术来实现的重新加载。一种常见的方式是通过自定义类加载器实现的重新加载。我们可以自定义一个类加载器,在加载之前,检查的更新时间。如果的源文件或字节码文件发生了改变,就重新载入这个。 另一种实现的重新加载的方式是使用Java的热部署技术,如JRebel。JRebel工具可以在不重新启动应用程序的情况下,动态地替换的字节码,从而实现的重新加载。 无论使用哪种方式,的重新加载都需要注意以下几点: 1. 确保重新加载与原始具有相同的名和包名,否则会导致运行时异常。 2. 在重新加载之前,需要确保原始已经被卸载,否则重新加载不会生效。 3. 对于正在运行的实例,需要注意处理好的版本兼容性,避免出现运行时错误。 4. 考虑到性能问题,的重新加载应该谨慎使用,尽量避免频繁地重新加载。 总之,Java重新加载可以通过自定义类加载器或使用热部署工具实现。但需要注意一些细节问题,确保重新加载能够正确地被使用。 ### 回答3: 在Java的重新加载是指在程序运行时对进行更新或替换。Java虚拟机(JVM)默认情况下不支持的重新加载,但可以通过一些特定的技术实现的重新加载。 一种常用的实现重新加载的技术是利用Java类加载器机制。Java类加载器可以动态加载文件,因此可以通过自定义类加载器来实现的重新加载功能。首先,需要编写一个自定义的类加载器,该加载器负责从指定的路径或URL加载文件。然后,在程序使用这个自定义的类加载加载需要重新加载文件,可以通过修改文件的路径或URL来实现的更新。在客户端代码,可以通过调用自定义类加载器的reload方法来重新加载。 另一种实现重新加载的技术是使用热部署框架,例如JRebel。这些框架通过在运行时动态地替换的字节码实现的重新加载。通过在IDE安装和配置这些框架,可以在开发过程实现代码的即时更新,节省了重新编译和启动的时间。 需要注意的是,的重新加载也可能会引发一些问题,例如由于之间的依赖关系导致的加载顺序问题,或者由于状态的改变导致的运行时错误。因此,在进行的重新加载时,需要谨慎对待,并进行充分的测试和验证。 总而言之,Java可以通过自定义类加载器或使用热部署框架来实现的重新加载。这些技术可以加快开发过程的代码更新和调试,提高开发效率。同时,在实际应用需注意之间的依赖和测试工作,以确保重新加载能够正确地运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值