【问题标题】:Cost of each class in Java application - Fewer huge classes or Several smaller onesJava 应用程序中每个类的成本 - 更少的大类或几个更小的类
【发布时间】:2014-03-01 04:33:41
【问题描述】:

对于添加到 Java 应用程序的每个新类,内存成本是多少?

  • 是拥有 5000 多个行的大型类还是几个 500-1000 个行的类更好(如果它们都已加载)
  • 每次实例化一个 Object 时,唯一的额外内存使用确实是用于实例变量引用
  • 对于一个没有实例变量的 5000 行类,加载该类时的成本规模是多少。类文件的大小是一个粗略的近似值吗?
  • jar 文件的大小是否表明类将占用的通常或最大内存大小?

在 cruftex 的回答后编辑: 这是我对类拆分的理解:

  • 分割成逻辑块可以很好地提高代码重用和 减少行数
  • 它还使其更易于理解和 维护代码

这是我现在对类加载的理解:

  • 第一次使用时类被加载到内存中(使用的内存大致是类文件的大小)
  • 如果使用 JIT,JIT 编译器会创建一些额外的机器友好的二进制版本,这会使用更多的内存
  • 如果使用 Hotspot,则仅使用机器友好版本对一些常用类进行优化(以平衡内存和速度)
  • 加载类后,创建额外实例的开销可以忽略不计(大约 50-100 字节?)(假设没有实例变量)
  • 一旦加载了一个类,该类本身就永远不会被垃圾回收

大致是这样工作的吗?

【问题讨论】:

  • 任何拥有超过 5000 行级别的人,尤其是如果必须由其他人维护,都应该被带回来并枪杀。这回答了你的问题了吗?不过说真的,您误认为程序的成本。主要是调试成本高,过早错误的优化也无法改善。如果你这样编码,你的尾巴就会有老虎。
  • 内存成本应该相同或非常接近,因为它取决于类中的对象。所以你可以通过做大量的类来为自己节省一两个字节,但代价是可读性。小班是更好的国际海事组织
  • 我的应用程序中有几个 5,000+ 行类。此外,不鼓励开设更多短班的做法。我正在尝试了解什么是“正确的事情”!
  • 我猜你遇到了比计算行数更紧迫的问题——比如一个严重需要重构和可能进行重大重组的程序。请浏览these books on refactoring 并考虑购买一两个。你不会后悔的。
  • 你说的很多问题都是对的。感谢您的链接。 (如果提到包含 scriptlet 和“字符串实习”的 5000 多行 JSP,我会得到更多的反对意见吗:O)

标签: java performance jakarta-ee memory


【解决方案1】:

对于添加到 Java 应用程序的每个新类,内存成本是多少?

通常没关系。一般来说,各种形式的代码只占用了总内存使用的一小部分(例如 5%)。因此,即使您确实设法将代码大小减少了一半,总体内存使用量也只会略微减少。

相比之下,过长的源文件会使代码库难以导航,而较大的作用域则更难了解类的确切作用以及某个更改是否安全。因此,较长的源文件会使修改代码的成本显着增加且容易出错。

  • 第一次使用时类被加载到内存中(使用的内存大致是类文件的大小)

正确。

  • 如果使用 JIT,JIT 编译器会创建一些额外的机器友好的二进制版本,这会使用更多的内存
  • 如果使用 Hotspot,则仅使用机器友好版本对一些常用类进行优化(以平衡内存和速度)

Hotspot 是一个 JIT,所以你在这里重复自己。但是,是的,JIT 确实会增加代码大小(但速度会更快)。

加载类后,创建额外实例的开销可以忽略不计(大约 50-100 字节?)(假设没有实例变量)

这是 JVM 特定的。在 Oracle Hotspot JVM 上,每个对象的内存开销约为 8 个字节,如下程序所示:

public class Test {
    public static void main(String[] args) {
        Object[] array = new Object[10_000_000];
        Runtime rt = Runtime.getRuntime();
        long usedBefore = rt.totalMemory() - rt.freeMemory();
        for (int i = 0; i < array.length; i++ ) {
            array[i] = new Object();
        }
        long usedAfter = rt.totalMemory() - rt.freeMemory();
        System.out.println(usedBefore);
        System.out.println(usedAfter);
        System.out.println((double)(usedAfter - usedBefore) / array.length);
    }
}
  • 一旦加载了一个类,该类本身就不会被垃圾回收

虽然 Java 语言规范没有强制要求,但我曾经使用过的每个 JVM 在其 ClassLoader 变得无法访问时都会释放一个类(当然,引导类加载器将始终保持可访问,但自定义 ClassLoader 可能变得无法访问)。

【讨论】:

  • 8字节超低..太棒了
【解决方案2】:

您的问题无法具体回答,所以我以一般方式尝试。您的问题针对内存消耗。但是,如果您节省内存,那么您的性能很可能会受到影响,这就是所谓的时空权衡。见:http://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff

节省内存的最简单方法 - 如果您关心您的程序大小 - 将完全关闭即时编译器。这样,您就可以为 Java 类的机器代码保存程序存储空间。顺便说一句:有很多 VM 选项会影响内存消耗!

拥有 5000+ 行的大类还是几个 500-1000 行的类更好(如果它们都被加载的话)

一般来说,您更有可能在较小的单元中更好地重用代码。这意味着,当您瞄准较小的尺寸时,您可能会节省代码行。

如果代码/方法保持不变,而您只是在类之间分配它们,则会为类增加额外的开销。

即使您就此询问三位专家并且他们查看了代码,他们也会得出不同的答案,因为这在很大程度上取决于您的真实代码、使用模式以及 JIT 在运行时对其执行的操作.

也就是说,即使是实验也可能将您引向错误的方向,因为您需要模拟相同的使用模式。

最后一点:看看你的虚拟机加载了多少类,然后决定是否真的值得保存一个视图。

每次实例化一个对象时,唯一的额外内存使用确实是用于实例变量引用

加上内存管理的持续开销。

对于一个没有实例变量的 5000 行类,加载该类时的成本规模是多少。类文件的大小是一个粗略的近似值吗?

没有 JIT:是的。

jar 文件的大小是否表明类将占用的通常或最大内存大小?

不,一点也不,因为类只在需要时加载。

一些建议:

如果您担心程序的内存使用情况,请先检查堆两次。我为此推荐 jmap 和 eclipse 内存分析器。

然后,你可以看看你的类和方法。您可以通过以下方式调试 JIT 并查看编译方法需要多少空间:-XX:-PrintCompilation。

【讨论】:

猜你喜欢
  • 2012-05-06
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
  • 2014-09-07
相关资源
最近更新 更多