【问题标题】:Finding Memory Usage in Java在 Java 中查找内存使用情况
【发布时间】:2009-02-17 05:08:29
【问题描述】:

以下是我需要解决的场景。我已经找到了两种解决方案。

我需要维护从数据库中获取的数据缓存,以便在 Swing GUI 上显示。 每当我的 JVM 内存超过其分配内存的 70% 时,我需要警告用户过度使用。一旦 JVM 内存使用率超过 80%,那么我必须停止所有数据库查询并清理作为用户操作的一部分获取的现有缓存并通知用户。在清理过程中,我将根据一些规则手动处理删除一些数据,并指示 JVM 进行 GC。每当 GC 发生时,如果内存清理并达到分配内存的 60%,我需要重新启动所有数据库处理并将控制权交还给用户。

为了检查 JVM 内存统计信息,我发现了以下两种解决方案。无法决定哪种方法最好以及为什么。

  1. Runtime.freeMemory() - 创建的线程每 10 秒运行一次并检查可用内存,如果内存超过上述限制,必要的弹出窗口将提示用户并调用方法来停止操作并释放记忆。

  2. MemoryPoolMXBean.getUsage() - Java 5 引入了 JMX 以在运行时获取内存快照。在,JMX 我不能使用阈值通知,因为它只会在内存达到/超过给定阈值时通知。唯一的使用方法是在 MemoryMXBean 中轮询并检查一段时间内的内存统计信息。

在使用轮询的情况下,对我来说这两种实现似乎都是相同的。

请提出这些方法的优点,以及是否有任何其他替代方案/对所用方法的任何更正。

【问题讨论】:

    标签: java memory memory-management garbage-collection


    【解决方案1】:

    附注:Runtime.freeMemory() 没有说明剩余分配的内存量,它只是当前分配的内存中的可用内存量(最初小于 VM 配置的最大内存量使用),但会随着时间的推移而增长。

    启动 VM 时,最大内存 (Runtime.maxMemory()) 仅定义了 VM 可以分配的内存上限(可使用 -Xmx VM 选项进行配置)。 总内存 (Runtime.totalMemory()) 是分配给 VM 进程的内存的初始大小(可使用 -Xms VM 选项进行配置),并且每次分配的内存超过其当前可用部分时都会动态增长 (Runtime.freeMemory() ),直到达到最大内存。

    您感兴趣的指标是可用于进一步分配的内存:

    long usableFreeMemory= Runtime.getRuntime().maxMemory()
        -Runtime.getRuntime().totalMemory()
        +Runtime.getRuntime().freeMemory()
    

    或:

    double usedPercent=(double)(Runtime.getRuntime().totalMemory()
        -Runtime.getRuntime().freeMemory())/Runtime.getRuntime().maxMemory()
    

    【讨论】:

      【解决方案2】:

      处理这类事情的常用方法是使用WeakReferences 和SoftReferences。你需要同时使用这两种方法——弱引用意味着你没有持有多个副本,而软引用意味着 GC 会一直挂在东西上,直到它开始耗尽内存。

      如果你需要做额外的清理,那么你可以添加对队列的引用,并覆盖队列通知方法来触发清理。这一切都很有趣,但您确实需要了解这些课程的作用。

      【讨论】:

        【解决方案3】:

        JVM 达到 100% 的内存使用率并在 GC 后返回到 10% 并每隔几秒执行一次是完全正常的。

        您不需要尝试以这种方式管理内存。 在运行完整的 GC 之前,您无法说出保留了多少内存。

        我建议你找出你真正想要达到的目标,并以另一种方式看待问题。

        【讨论】:

        • IMO,这不完全正常。可以调整这些参数(池大小、完整 GC 间隔等)
        【解决方案4】:

        您提到的要求与垃圾收集在 JVM 中的工作方式明显矛盾。

        由于 JVM 的行为,很难以正确的方式警告您的用户。 完全停止 als 数据库操作,清理东西并重新开始确实不是要走的路。

        让JVM做它应该做的事情,为你处理所有相关的内存。 现代的 JVM 非常擅长它,通过对 GC 参数进行一些微调,您将获得更干净的内存处理,然后自己强制处理

        http://www.kodewerk.com/advice_on_jvm_heap_tuning_dont_touch_that_dial.htm 之类的文章提到了优缺点,并很好地解释了 VM 为您做了什么

        【讨论】:

          【解决方案5】:

          类似的任务我只用了第一种方法,没问题。

          对于这两种方法,您应该注意的一点是实现某种去抖动 - 即,一旦您发现您已经达到 70% 的内存,请等待一分钟(或您认为合适的任何其他时间) - GC 可以在那个时候运行并清理大量内存。

          如果您在系统中实现 Runtime.freeMemory() 图,您将看到内存如何不断地上下波动。

          【讨论】:

            【解决方案6】:

            VisualVM 比 JConsole 好一点,因为它为您提供了一个漂亮的可视垃圾收集器视图。

            【讨论】:

              【解决方案7】:

              查看 JConsole。它绘制了您需要的信息,因此只需根据您的需要进行调整(假设您在 Sun Java 6 上运行)。

              这还允许您将监视过程与您想要查看的内容分开。

              【讨论】:

                【解决方案8】:

                我知道,在原始帖子之后很晚,但我想我会发布一个示例来说明我是如何做到的。希望它对某人有用(我强调,这是主要示例的证明,仅此而已……也不是特别优雅:))

                只需将这两个函数放在一个类中,它应该可以工作。

                编辑:哦,还有import java.util.ArrayList; import java.util.List;

                public static int MEM(){
                    return (int)(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory() +Runtime.getRuntime().freeMemory())/1024/1024;
                }
                
                public static void main(String[] args) throws InterruptedException
                {
                    List list = new ArrayList();
                
                    //get available memory before filling list
                    int initMem = MEM();
                    int lowMemWarning = (int) (initMem * 0.2);
                    int highMem = (int) (initMem *0.8);
                
                
                    int iteration =0;
                    while(true)
                    {
                        //use up some memory
                        list.add(Math.random());
                
                        //report
                        if(++iteration%10000==0)
                        {
                            System.out.printf("Available Memory: %dMb \tListSize: %d\n", MEM(),list.size());
                
                            //if low on memory, clear list and await garbage collection before continuing
                            if(MEM()<lowMemWarning)
                            {
                                System.out.printf("Warning! Low memory (%dMb remaining). Clearing list and cleaning up.\n",MEM());
                
                                //clear list
                                list = new ArrayList();  //obviously, here is a good place to put your warning logic
                
                                //ensure garbage collection occurs before continuing to re-add to list, to avoid immediately entering this block again
                                while(MEM()<highMem)
                                {
                                    System.out.printf("Awaiting gc...(%dMb remaining)\n",MEM());
                                    //give it a nudge
                                    Runtime.getRuntime().gc(); 
                                    Thread.sleep(250);
                                }
                
                                System.out.printf("gc successful! Continuing to fill list (%dMb remaining). List size: %d\n",MEM(),list.size());
                                Thread.sleep(3000); //just to view output
                            }
                        }
                    }
                }
                

                编辑:但是,这种方法仍然依赖于使用 -Xmx 在 jvm 中合理设置内存。

                EDIT2:似乎 gc 请求行确实对事情有所帮助,至少在我的 jvm 上是这样。 ymmv。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2014-12-07
                  • 2012-01-03
                  • 1970-01-01
                  • 2014-04-10
                  • 1970-01-01
                  • 2011-01-30
                  • 2018-01-16
                  • 1970-01-01
                  相关资源
                  最近更新 更多