【问题标题】:Behavior of creating objects in ColdFusion在 ColdFusion 中创建对象的行为
【发布时间】:2014-08-29 14:28:42
【问题描述】:

曾经我有一个理论,即在每个请求上实例化对象而不是让它们驻留在应用程序范围内是一个巨大的内存消耗。随着多年来我对 ColdFusion 的了解不断增长,我认为我并不真正了解 CF 是如何处理 CF 框架的“黑匣子”中的类的,所以我将向社区寻求纠正或确认。

我只是要扔掉我认为正在发生的事情:

  1. CFC 被编译成一个类,该 CFC 中的每个方法都被编译成一个类。
  2. 这些类将驻留在 (PermGen) 内存中,并且可以根据 CF 管理员设置写入磁盘。
  3. 创建新对象或请求模板时,会对源代码进行哈希处理,并与编译后的类中存储的哈希值进行比较。
    1. 如果有匹配,它将使用内存中的编译类
    2. 如果编译的类不存在,则从源代码编译
    3. 如果编译的类存在,但哈希不匹配,则会重新编译。
  4. 顺便说一句,每当您启用可信缓存时,ColdFusion 将不再对源进行哈希处理以检查差异,而是继续使用内存中的已编译类。
  5. 每当您创建一个新对象时,您都会获得一个指向已编译类及其方法类的新指针,并且任何运行时事件都会在伪构造函数中发生。 编辑:此时,我指的是使用 createObject 并运行函数之外的任何“松散”代码。当我说指针时,我指的是对分配给对象范围(this、变量、函数变量)的内存的引用。
  6. 如果您请求初始化,则构造函数运行。此时消耗的内存只是您的新引用以及伪构造函数和构造函数中设置的任何变量。您实际上并没有为整个班级的副本占用内存。 编辑:对于这一步,我指的是使用 new 运算符或链接您的 createObject().init() 老派。

这消除了我个人多年来可能听说过的一个巨大谬误,即在每个请求中实例化大对象是一个巨大的内存消耗(由于拥有类的副本而不仅仅是参考)。请注意,我不赞成这样做,单例模式很神奇。我只是想确认幕后发生的事情,以防止在遗留代码中追逐红鲱鱼。

编辑:感谢大家的意见,这对我来说真的很有帮助。

【问题讨论】:

  • 我不知道,但 2 号看起来不太可能。
  • @Dan,我以为他们只是加载到 PermGen 中。
  • 好问题!!!我有一个项目需要正确回答这个问题。谢谢你问得这么准确。

标签: coldfusion


【解决方案1】:

我已经开发 CF 14 年了,我从未听说过有人声称在每个请求上创建 CFC 实例会由于类编译而消耗内存。在 Java 级别,您的 CFML 代码直接编译为字节码并作为 Java 类存储在内存和磁盘中。 Java 类不存储在堆中,而是存储在永久代中,永久代(通常)不是收集的内存空间。您可以创建该 CFC 的尽可能多的实例,并且不会使用更多的 perm gen 空间,但是将分配堆空间来存储该 CFC 在其存在期间的 instance 数据。请注意,开源 Railo 不为方法使用单独的类。

现在,如果您为此创建大量 CFC 实例(或任何变量),则会在堆的年轻代中产生大量垃圾。只要在请求完成后没有保留硬引用,这些对象就会在下一次次要垃圾回收运行时从堆中清除。这不一定是坏事,但在调整应用程序的性能时应始终考虑堆大小和 GC 暂停。

现在,有理由持久化 CFC 实例,无论是作为单例模式还是在会话、请求等期间。一个原因是实际对象创建的开销。这通常涉及磁盘 I/O 以检查上次修改时间。自过去以来,对象创建的速度显着提高,但如果您要创建数千个实例,仍然远远落后于原生 Java。另一个主要原因是您的对象在应用程序/会话/请求的生命周期内保持状态,例如在用户购物时存储在会话中的购物车。

为了完整起见,我将尝试明确地解决您的观点:

  1. 对于 Adob​​e CF 是,对于 Railo,方法是内部类
  2. 是的。
  3. 实际上,我认为不涉及任何散列。这一切都基于源文件上最后修改的日期时间。
  4. 是的,但同样,没有散列 - 它只是跳过磁盘 I/O 以检查最后修改的日期时间
  5. 我不认为“指针”是正确的术语,因为这意味着 Java 类实际上存在于堆中。 CF 使用自定义 URL 类加载器为模板加载类,然后创建该类的实例并将其存储在堆中。我可以理解这可能会令人困惑,因为 CFML 没有“类”的概念。一切都只是一个实例或根本不存在。我不确定您所说的“运行时事件在伪构造函数中发生[ing]”是什么意思。
  6. 需要明确的是,JAVA 构造函数在您创建 CFC 的那一刻就已经运行。 CF 构造函数可能是可选的,但它对 CFC 实例消耗的内存的影响为零。同样,我认为您也不必要地挂断了伪构造函数。这只是组件内部的松散代码,它在创建时运行,与堆中分配的内存无关。 Java 类从不复制,它只是实例的模板。

【讨论】:

  • 是的,我不相信下面会复制课程。
  • Railo 是的,因为它是开源的,而且我已经阅读了代码。至于 Adob​​e CF,我找不到任何官方文档,但我已经查看了很多正在加载的应用程序的堆栈跟踪,如果没有受信任的缓存,您会看到许多对磁盘子系统的调用以检索日期文件的最后修改。如果使用任何其他过程(例如散列),则无需获取该数据。此外,这很有意义。读取整个文件的开销会大得多。
  • 布拉德说得对。据我所知,没有人曾将明显的内存消耗作为支持或反对对象重用的论据,而不是创建瞬态@突发奇想。案件一直是业绩之一。我认为布拉德错过的一件事是,人们不应该根据这样的感知惩罚来构建自己的 CFML 应用程序:如果它们出现,就处理它们。总的来说,当您需要瞬态时创建一个瞬态,在有意义的情况下将一个实例持久化在共享范围中,例如:UserService 可能会持续应用程序的生命周期;只要需要,用户对象就会持续存在。
  • 请注意,“巨型 CFC”并不意味着“更多内存”,只是“巨型” .class 文件(在磁盘上和 PermGen 中),但坦率地说,与常规 Java 相比,PermGen 通常很小这些天堆(我们使用 8GB 堆和 1GB PermGen 运行)。 CFC instance 包含一个 hashmap(变量范围),它将使用与您的代码一样多的内存 - 与代码的大小无关。一个重要的注意事项:每个方法实际上是一个(小)Java 类的实例,并且这些实例被添加到变量作用域中——方法调用是动态的。
  • 只是为了澄清我的“方法是一个对象”评论:在运行时,CFML 中的函数是您可以传递、分配给变量等的值 - 它们是带有“ invoke()" 方法调用它们的实际编译代码(在 PermGen 中)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多