在Java中查找内存使用情况

时间:2009-02-17 05:08:30

标签: java memory memory-management garbage-collection

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

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

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

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

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

  3. 如果使用轮询,对我来说似乎两个实现都是相同的。

    请说明这些方法的优点,以及是否有任何其他替代方法/使用方法的任何更正。

8 个答案:

答案 0 :(得分:14)

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

启动VM时,最大内存(Runtime.maxMemory())只定义VM可以分配的内存上限(可使用-Xmx VM选项进行配置)。 总内存(Runtime.totalMemory())是为VM进程分配的内存的初始大小(可使用-Xms VM选项进行配置),并且每次分配超过其当前空闲部分时将动态增长(Runtime.freeMemory()){ {1}}),直到达到最大记忆。

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

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

或:

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

答案 1 :(得分:7)

处理此类事情的常用方法是使用WeakReferenceSoftReference。你需要同时使用 - 弱引用意味着你没有持有多个东西副本,而软引用意味着GC将挂起,直到它开始耗尽内存。

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

答案 2 :(得分:5)

对于JVM来说,内存使用率达到100%是完全正常的,并且在GC之后回复说10%,并且每隔几秒就会这样做。

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

我建议你弄清楚你真正想要实现的目标,并以另一种方式看待问题。

答案 3 :(得分:4)

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

由于JVM的行为,以正确的方式警告用户将非常困难。 完全停止als数据库操作,清理东西并重新启动真的不是可行的方法。

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

http://www.kodewerk.com/advice_on_jvm_heap_tuning_dont_touch_that_dial.htm这样的文章提到了优点和缺点,并提供了一个很好的解释虚拟机为你做什么

答案 4 :(得分:2)

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

对于这两种方法,你应该注意的一件事是实现某种去抖 - 即一旦你认识到你已经达到了70%的内存,等待一分钟(或者你认为合适的任何其他时间) - GC可以在那个时候跑,并清理大量的记忆。

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

答案 5 :(得分:1)

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

答案 6 :(得分:0)

查看JConsole。它描绘了您需要的信息,因此需要根据您的需求进行调整(假设您使用的是Sun Java 6)。

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

答案 7 :(得分:0)

原帖后很晚,我知道,但我想我会发一个例子,说明我是怎么做到的。希望它对某人有用(我强调,它是主要例子的证据,没有别的......也不是特别优雅:))

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

编辑:哦,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上。因人而异。