2009-02-17 29 views
6

以下是我需要解决的情况。我有两个解决方案。在Java中查找内存使用情况

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

为了检查JVM内存统计信息,我找到了以下两种解决方案。无法确定哪个是最好的方式以及为什么。

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

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

在使用轮询的情况下,对我来说这两种实现看起来都是一样的。

请建议的方法的优点,如果有任何其他替代品/方法使用的任何更正。

回答

2

我只使用第一种方法进行类似的任务,它是确定的。

对于这两种方法,你应该注意的一件事是实施某种类型的反弹 - 即一旦你认识到你已经击中了70%的内存,等待一分钟(或任何其他时间,你觉得合适) - GC可以在那个时候运行并清理大量的内存。

如果你在你的系统中实现了一个Runtime.freeMemory()图形,你将会看到内存是如何不断地向上和向下,向上和向下。

5

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

您不应该尝试以这种方式管理内存。 在完整的GC运行之前,您不能说保留了多少内存。

我建议你研究一下你真正努力实现的目标,并以另一种方式来看问题。

+0

IMO,这并不完全正常。这些参数可以调整(池大小,完整的GC间隔等) – 2009-02-17 07:36:51

14

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

启动虚拟机时,最大内存(Runtime.maxMemory())仅定义了虚拟机可以分配的内存上限(可使用-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() 
4

你提到与垃圾收集的是如何工作的一个明显的矛盾的要求JVM。

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

让JVM做它应该做的事,处理与您相关的所有内存。 JVM的现代几代人都非常好它,并用GC参数的一些细化和微调,您将得到AA干净得多的内存处理,然后强迫的事情自己

文章像http://www.kodewerk.com/advice_on_jvm_heap_tuning_dont_touch_that_dial.htm提的利弊,并提供了一个很好的解释了什么虚拟机为你服务

0

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

这也可以让你从你想看的东西中分离出监视过程。

7

处理这类事情的通常方法是使用WeakReference s和SoftReference s。你需要同时使用 - 弱引用意味着你没有持有多个副本,软引用意味着GC将挂起东西,直到它开始用完内存。

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

1

VisualVM比JConsole好一点,因为它给你一个很好的视觉垃圾收集器视图。

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的内存设置合理不过。

编辑2:似乎gc请求行真的帮助一些事情,至少在我的jvm。因人而异。