如何提高Java应用程序的内存效率?

如何提高Java应用程序的内存效率?,java,memory,caching,heap,objectpool,Java,Memory,Caching,Heap,Objectpool,如何优化具有大量(数百万)长寿命对象的应用程序的堆大小使用?(大缓存,从数据库加载大量记录) 使用正确的数据类型 避免使用java.lang.String来表示其他数据类型 避免重复的对象 如果预先知道值,则使用枚举 使用对象池 String.intern()(好主意?) 仅加载/保留所需的对象 我正在寻找通用编程或Java特定的答案。没有时髦的编译器开关 编辑: 优化可以在堆中出现数百万次的POJO的内存表示形式 用例 在内存中加载一个巨大的csv文件(转换为POJO) 使用hi

如何优化具有大量(数百万)长寿命对象的应用程序的堆大小使用?(大缓存,从数据库加载大量记录)

  • 使用正确的数据类型
    • 避免使用java.lang.String来表示其他数据类型
  • 避免重复的对象
    • 如果预先知道值,则使用枚举
    • 使用对象池
    • String.intern()(好主意?)
  • 仅加载/保留所需的对象
我正在寻找通用编程或Java特定的答案。没有时髦的编译器开关

编辑:

优化可以在堆中出现数百万次的POJO的内存表示形式

用例

  • 在内存中加载一个巨大的csv文件(转换为POJO)
  • 使用hibernate从数据库中检索数百万条记录
答覆摘要:

  • 使用飞锤模式
  • 抄写
  • 使用3个大小为10M的数组(或其他数据结构)是否比加载具有3个属性的10M对象更有效?(操作数据可能会很痛苦,但如果你真的内存不足…)

    • 您没有说明要存储哪种类型的对象,因此很难提供详细的建议。然而,一些(非排他性)方法(无特定顺序)是:

      • 在任何地方使用 可能
      • 缓存到磁盘。有 缓存解决方案 爪哇
      • 关于是否 实习是个好主意。看见 我想问你一个问题。 String.intern(),以及 围绕其适用性展开辩论
      • 利用或 用于存储您可以访问的数据的引用 按需重新创建/重新加载。看见 关于如何使用软 使用缓存技术的引用

      更多地了解所存储对象的内部结构和生命周期将得到更详细的答案。

      我建议您使用内存探查器,查看内存消耗的位置,并对其进行优化。如果没有定量的信息,你最终可能会改变一些事情,这些事情要么没有效果,要么实际上会让事情变得更糟

      您可以考虑更改数据的表示形式,尤其是在对象很小的情况下。
      例如,可以将数据表表示为一系列列,每列具有对象数组,而不是每行一个对象。如果不需要表示单个行,那么这可以为每个对象节省大量开销。e、 g.一个包含12列和10000000行的表可以使用12个对象(每列一个),而不是1000万个对象(每行一个)

      确保对象模型的正常化,不要重复值


      嗯,而且,如果只有数百万个对象,我想我会选择一个像样的64位虚拟机和大量ram;)

      我想补充一点Peter alredy提出的观点(不能对他的答案发表评论:()使用内存分析器(检查)总比使用直觉要好。80%的时候,我们忽略的例行程序有一些问题。此外,集合类更容易发生内存泄漏。

      普通的“分析器”不会对您有多大帮助,因为您需要所有“活动”对象的概览。您需要堆转储分析器。我建议使用

      检查重复的对象,从字符串开始。
      检查您是否可以应用flightweight、copyonwrite、惰性初始化等模式(谷歌将是您的朋友)。

      您可以在内存中存储更少的对象。:)使用溢出到磁盘的缓存或使用Terracotta对堆进行群集(这是虚拟的)允许将未使用的部分从内存中清除,并透明地将故障恢复到内存中。

      一个奇特的方法:将大部分数据压缩到ram中。仅展开当前工作集。如果您的数据具有良好的局部性,则可以很好地工作

      使用更好的数据结构。java中的标准集合相当占用内存

      [什么是更好的数据结构]

      • 如果您查看集合的源代码,就会发现如果您限制自己访问集合的方式,就可以为每个元素节省空间
      • 集合句柄增长的方式不适合大型集合。复制太多了。对于大型集合,您需要一些基于块的算法,如btree
      花一些时间熟悉和调整,尤其是那些与垃圾收集有关的。虽然这不会改变对象使用的内存,但它会对内存密集型应用程序的性能产生很大影响。如果您有数百万个整数和浮点数等,请查看您的算法是否允许在基元数组中表示数据。这意味着每次垃圾收集的引用更少,CPU成本更低

    • 为所有不再使用的
      变量
      赋值为空
      值。因此,
      使其可用于垃圾收集
    • 一旦使用结束,就取消引用集合
      ,否则GC不会清除这些集合

    • 请看从这里链接的演示文稿。它列出了常见java对象和原语的内存使用,并帮助您了解所有额外内存的去向

      1)尽可能使用正确的数据类型

      Class Person {
       int age;
       int status;
      }
      
      在这里,我们可以在发送Person对象时使用以下变量来节省内存

      class Person{
        short age;
        byte status;
      }
      
      2) 而不是返回新的ArrayList();从方法中,可以使用Collection.emptyList(),它将只包含一个元素,而不是默认的10个元素

      例如

      public ArrayList getResults(){
          ..... 
          if(failedOperation)
              return new ArrayList<>();
      }
      //Use this
      public ArrayList getResults(){
          if(failedOperation)
             return Collections.emptyList();
      }
      
      public ArrayList getResults(){
      ..... 
      如果(失败操作)
      返回新的ArrayList();
      }
      //用这个
      公共ArrayList getResults(){
      如果(失败操作)
      返回集合。emptyList();
      }
      
      3)尽可能在方法中移动对象的创建,而不是静态声明,因为对象的字段将存储在堆栈而不是堆上

      4) 使用protobuf、t等二进制格式