当前位置:首页 > CN2资讯 > 正文内容

Java我的世界卡

3天前CN2资讯


一些背景:我们难道不能和谐相处吗?

自从二十世纪九十年代Java首次创建的时候,Java资源和类的加载就已经是一个问题了。通过增加启动和初始化的次数,Java应用程序服务器放大了这个问题。为了缓解这个这个问题,程序员们做了很多的努力,其中包括exploded deployment到应用程序服务器的方法,但它只是在非常小的程序中才会起作用,还有2001年创建的Java HotSwap。HotSwap启用时,可以让你已有的方法中立马让你的更改生效。一般它应用在调试session中,但是因为方法边界的限制它并不是总是有效的。构建、部署和重启经常要等上一段时间,甚至等5到15分钟也是经常发生的。对更大的应用程序服务器,就容易引起更严重的问题。

问题

一旦一个Java类被一个类加载器加载,它就是不可变的并且和类加载器的存在时间一样久。唯一的标识就是类名类加载器,所以为了重新加载一个应用程序,你实际上需要创建一个新的类加载器,这个新创建的类加载器用来加载最新版本的应用程序的类。你不能将一个新类映射到现有对象中,所以在重新加载时迁移状态是很重要的。这可能意味着要重新初始化应用程序和配置状态等,对用户会话状态复制,从而重新创建整个应用程序对象图。往往这也是耗费时间的,而且也很容易引起内存泄漏。

当用类加载器来处理内存泄露时,一行代码的小泄露通过Java使用的引用模型可能被放大。比如,一个类加载器实例会针对它所加载的所有类和随后被创建的对象的实例都拥有一个引用。所以即使是一个小泄露,或许是在重载期间在应用程序实例之间的状态迁移时被加入的,都可能会产生很大的影响。

所以,作为一个开发者对你来说这意味找什么呢?这意味着持续的编译、构建、打包、重新部署、以及应用程序服务重新启动会妨碍你的专注和有趣的卓有成效的工作。

这篇文章旨在向开发者阐明 JRebel的奥秘所在,一探这个产品在幕后所做的事情,与此同时让你注意到你可能忽略的或者认为理所当然的JVM的其他方面。这篇文章将更多的关注 JRebel解决的主要问题。

让我们看看类加载器

一个类加载器只是一个普通的Java对象

对,和JVM中的系统类加载器相比它一点也不高明,一个类加载器只是一个Java对象。它是一个抽象类,CLassLoader,能够被你创建的类所继承。下面就是API:


public abstract class ClassLoader { public Class loadClass(String name); protected Class defineClass( byte [] b); public URL getResource(String name); public Enumeration getResources(String name); public ClassLoader getParent(); }


看起来相当的简单,是不是?让我们看看这个类的方法。这个中心方法是loadClass,它有一个String类型的class类型参数并且返回一个实际的Class对象。如果你以前使用类加载器那么它方法可能是你最熟悉的方法,因为它是日常编码中最常用的。defineClass 是JVM中一个final类型的方法,它需要一个从字节数组的参数,参数来自于一个文件或者在网络中的一个位置,并且产生相同的输出,一个Class对象。

一个类加载器也可以从classPath(类路径—一个环境变量)中找到资源。它和loadClass方法起作用的方式类似。有很多方法getResourcegetResources,他们返回一个URL或者是一个枚举类型的URLS,这些URL指向代表传递给方法的参数的名称的资源。

每一个类加载器都有父类;getParent 方法返回了一个类加载器的父类,这个不是Java继承的关系,而是通过一个链表方式连接。我们稍后将稍稍深入的看一下这个问题。

类加载器是懒惰的,所以类只有在运行时被需要才会被加载。类是在当被资源请求它的时候才被加载,所以在运行时一个类可能被多个类加载器加载,这取决于它们从哪里被引用以及哪些类加载器加载这些类……哎呀,我已经和它对上眼了!让我们看看一些代码。


public class A { public void doSmth() { B b = new B(); b.doSmthElse(); } }


我们让类A在doSmth方法中调用类B的构造器。下面说明了发生了什么


A. class .getClassLoader().loadClass(“B”);



最开始加载类A的类加载器被请求去加载类B。

类加载器是分层次等级的,可是像孩纸们一样,他们却不经常请求他们的父类。

每一个类加载器拥有一个父加载器。当向一个类加载器请求一个类的时候,它通常会直接跑到父类加载器那里,调用loadClass方法。如果两个具有相同父类加载器的类加载器被要求去加载相同的类,它将只由父类进行一次。当两个类加载器分别的去分别加载相同的类的时候会变得很麻烦,因为这可能会导致问题,我们稍后再看看。

当一个J2EE应用程序服务器实现了一个J2EE规范的时候,他们中的一些更愿意把这个任务委派给父类,而其他则选择首先在WEB应用程序类加载中查找。让我们更加深入这一点,使用图1作为我们的例子。


在这个例子中,模块 WAR1 有它自己的类加载器而且更愿意去用它自己来加载类而不是委派给他的父类,这个类加载器被App1.ear限定了作用域。这意味着不同的WAR模块,像WAR1和WAR2一样,不能看见彼此的类。App1.ear 模块有他自己的类加载器并且它是WAR1和WAR2类加载器的父类。当WAR1和WAR2需要沿着层级层次向上发出请求时(也就是需要WAR类加载器作用域外的类时),App1.ear类加载器被WAR1和WAR2使用。当两个都存在的时候,WAR的类会覆盖EAR的类。最后EAR类加载器的父类是容器类加载器。EAR类加载器将委派请求发给容器类加载器,但是它不会像WAR类加载一样使用相同的方法,因为EAR类加载器实际上会向上委托请求而不是通过本地的类。正如您所看到的,这是相当难以理解的,和普通的JavaSE的类的加载行为是不一样的。

我们应该怎么重新加载应用程序中的类

通过先前我们查看的类加载器API我们知道,你只能装载类。也就是说,没有其他的方式去卸载或者重新加载这些类,所以为了在运行时重新创建一个类,你实际上必须抛弃整个类的层次结构然后重新创建它,这样才能加载新的类,并在运行的时候使用它,正如图2 展示的一样:


如果你已经有过一段的Java编程经历,你知道内存泄露经常会发生的。譬如集合含有指向对象的引用,本应该清除引用,但是没有清除。类加载器是一种很特殊的情况,不幸的是在Java平台的现状中,这些泄露既是难以避免的又是代价很大的:在几次重部署后,应用程序经常导致outofmemoryerror错误。

每一个对象都有一个指向它的类的引用,也就是有一个指向它的类加载器的引用。关键在于通过这个每一个类加载器都有一个指向它加载的每一个类的引用,这些类都拥有静态域,如图3。


这意味着:

1.如果一个类加载器内存泄露了,那么它会占用它加载的所有类和它们所有的静态域。静态域通常含有缓存、单例对象和不同的配置以及应用程序状态。即使你的应用程序没有一些大的静态缓存,这也不意味着你使用的框架不占用着它们(如Log4J是一种常见的罪魁祸首,因为它通常是放在服务器类路径中)。这就说明了为什么加载器泄露的代价会很大。

2.类加载器发生内存泄露很容易,只要类加载器加载了一个类,类创建了一个对象,然后给对象一个引用就可以了。就算对于一个看起来无害的对象(譬如没有域),但是它也会保留它的类加载器和所有应用程序的状态。就算在部署中没有出现问题,但是没有做适当的清理工作,这个也足够去埋下了泄露的隐患。在一个典型的应用程序将会有几个这样的地方,因为第三方库构建的原因,其中一些几乎不可能修复。因此,类加载器泄露是相当普遍的。

这就是存在的技术问题。为了更新我们在运行时的代码,我们通常需要建立,打包,重新部署,甚至重启容器看到更新的代码。接下来,我们将看看针对Java中这一核心问题的解决方法,包括在Java 1.4中引入的HotSwap类重载框架和JRebe的解决方法。

关于作者(SIMON MAPLE):

Simon是一个ZeroTurnaround的技术人员,他喜欢讨论和交流,不喜欢说教。他对技术社区投入了极大的热情,他既是伦敦Java社区组织(London Java Community, LJC)的成员,也是LJC JCP EC委员会的一员。Simon过去在IBM从事WebSphere Application Server项目的测试、开发以及技术教学的工作,一共超过了十年,那之后他加入了ZeroTurnaround。他喜欢看足球比赛(各种各样的足球),踢足球,喝茶还有陪伴家人。


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/21485.html

    分享给朋友:

    “Java我的世界卡” 的相关文章

    探索宝塔的历史与文化:传承与创新的结合

    宝塔,这种在中国传统文化中扮演着重要角色的建筑,具有悠久而丰富的历史。我曾在一次旅行中惊叹于那些巍峨耸立的宝塔,仿佛它们在诉说着古老的故事。它们的起源可以追溯到佛教传入中国之前,实际上,宝塔最早的样式源自印度,梵语中称之为“窣堵坡”,主要用于供奉佛陀的舍利和进行宗教仪式。 在东汉时期,佛教逐步传入中...

    搬瓦工官网是哪个?全面解析搬瓦工的官方链接和服务

    搬瓦工官网是哪个? 当我提到“搬瓦工”,很多朋友可能会想知道它的官网到底在哪儿。其实,搬瓦工的主官网地址是 bandwagonhost.com。不过,有时访问这个主域名可能会遇到点小问题。为了方便用户,搬瓦工还提供了几个官方镜像站,包括 bwh1.net、bwh8.net、bwh81.net、bwh...

    全面解析甲骨文服务器:功能、应用与未来趋势

    甲骨文服务器概述 甲骨文服务器的定义与功能 甲骨文服务器,简单来说,是一种数据库管理系统,非常适合于处理复杂的数据和海量的信息。它不仅能够存储数据,更能够提供高效的数据管理、查询和分析功能。这样的系统通常由多个功能模块构成,能够满足企业在数据处理方面的多种需求。我一直认为,甲骨文服务器最大的优势在于...

    vi和vim的区别:选择合适的文本编辑器

    vi与vim的基本概述 在我初次接触文本编辑器时,vi这个名字总是出现在我的眼前。vi是一个非常经典的编辑器,诞生于1976年,最初是为了在Unix系统上进行文本编辑而设计的。它的界面简单,但功能强大,支持基本的文本编辑需求。我常常在命令行中打开vi,只有最基本的命令,但却能完成许多编辑工作。这种简...

    海外云电脑的优势与使用技巧:全面解析云端计算服务

    云电脑,一个如今越来越受欢迎的概念,指的是通过网络将计算资源进行虚拟化,实现远程访问和使用。如果把传统电脑比作我们的家,云电脑就像是一个随时随地可以打开的大门,让我们能够使用强大的计算资源而无需依赖本地设备。简单来说,我可以在任何可以上网的地方,通过云端的服务器,使用重型软件或进行大规模计算,而不需...

    Cloudflare API教程:全面掌握网站加速与安全管理

    Cloudflare API概述 什么是Cloudflare API? Cloudflare API 是一个强大的工具,可以让开发者与 Cloudflare 的各项服务进行直接互动。换句话说,Cloudflare 提供了一个丰富的接口,使我们可以通过程序控制和管理云服务,而不仅仅依赖于图形化用户界面...