【问题标题】:Java 8 full GC and OutOfMemory Java heap spaceJava 8 全 GC 和 OutOfMemory Java 堆空间
【发布时间】:2017-09-07 14:16:54
【问题描述】:

使用 VisualVM 并检查 Tomcat 8.5 catalina.out 日志我每次都看到 几乎(7 出11 次左右)当 full GC 发生时,日志显示 OutOfMemory(在同一分钟)。

使用与内存管理有关的 Tomcat 参数-Xms3G -Xmx=6G -XX:+UseG1GC -XX:+UseStringDeduplication -XX :MaxHeapFreeRatio=100

起初我以为是因为默认的 -XX:MaxHeapFreeRatio 值是 70,因为我看到了最大值。在 full GC 期间,堆大小(当然还有已用堆)会显着下降 - ~10-20%。但是添加 XX:MaxHeapFreeRatio=100 并没有解决问题。

虽然这是内存使用图不同JVM参数(无法获得具有旧 JVM 参数的 ATM)在完全 GC 内存使用率快速增长后,它在某种程度上是相似的,最大相同。堆大小和最大值。堆大小不会下降。

您知道为什么会发生这种情况吗?

更新:我忘了提到之前的 full GCOutOfMemory当堆大小甚至未满 - ~5GB 时会发生。那时我没有一次看到堆达到 6GB。

【问题讨论】:

  • 你应该调整那些 JVM GC 参数:blog.sokolenko.me/2014/11/javavm-options-production.html
  • 在问这样的问题之前,做一些分析,例如你可以使用 YourKit。在您的情况下,这可能是由于 GC 无法跟上在短时间内创建和释放的太多对象。

标签: java tomcat garbage-collection out-of-memory


【解决方案1】:

显然有些创建的对象不能被正确地垃圾回收。您可以尝试使用 VisualVM 的采样器功能并跟踪创建的实例数。

【讨论】:

  • 你是对的,这就是 OOM 的意思,但是当剩余大约 1GB 的可用空间(请参阅问题更新)并且同时发生完全 GC 时,如何发生 OOM。
  • 这可能有几个原因。比如如果 permgen 空间已满,或者没有可用的连续内存块用于创建大型数组或对象,或者如果 GC 花费的时间太长,数组大小超出了 jvm 允许的限制等。
【解决方案2】:

尝试使用 MapDB 缓存 IO-Operations。

您可以这样做将其缓存到基于磁盘的文件数据库中:

import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.mapdb.DB;
import org.mapdb.DBMaker;

/**
 * Singleton class.
 */
public class DBManager 
{
        /**
     * Variables.
     */
    private static File dbFile = new File("path/to/file");
    private DB db;
    private static final String password = "yourPassword";
    private Map<Integer, String> ctDB;
    private static DBManager manager;

   /**
    * Singleton operations.
    */

  /**
   * Static initializer.
   */
  static
  {
     manager = null;
  }

/**
 * Singleton method @see DBManager.getInstance(File dbFile);
 * @return          ->  An object / instance of this class.
 */
public static DBManager getInstance()
{       
    if(isFileDatabaseOK())
    {
        /**
         * Check if an object/instance from this class exists already. 
         */
        if(manager == null)
        {
            manager = new DBManager();
        }

        /**
         * Return an object/instance of this class.
         */
        return manager;
    }
    else
    {
        return null;
    }
}

/**
 * Constructors.
 */

/**
 * Empty default Constructor starts the MapDB instance.
 */
private DBManager() 
{       
    /**
     * Load the database file from the given path
     * and initialize the database.
     */
    initMapDB();
}

/**
 * MapDB initializer.
 */

/**
 * Initialize a MapDB database.
 */
private void initMapDB() 
{
    /**
     * Persistence: Make MapDB able to load the same database from the 
     * file after JVM-Shutdown. Initialize database without @see     org.mapdb.DBMaker.deleteFilesAfterClose()
     * @see <link>https://groups.google.com/forum/#!topic/mapdb/AW8Ax49TLUc</link>
     */
    db = DBMaker.newFileDB(dbFile)
            .closeOnJvmShutdown()       
            .asyncWriteDisable()
            .encryptionEnable(password.getBytes())
            .make();

    /**
     * Create a Map / Get the existing map.
     */
    ctDB = db.getTreeMap("database");
}

/**
 * File existence check.
 * If file doesn't exists -> Create a new db file and inform the user.
 */
private static boolean isFileDatabaseOK() 
{       
    /**
     * If the file doesn't exists (First run) create a new file and 
     * inform the user.
     */
    if(!dbFile.exists())
    {
        try 
        {
            dbFile.getParentFile().mkdirs();
            dbFile.createNewFile();

            /**
             * TODO 
             * System.out.println("Database not found. Creating a new one.");
             */

            return true;
        }
        catch (IOException e)
        {
            /**
             * TODO Error handling
             */
            e.printStackTrace();
            return false;
        }
    }
    else
    {           
        return true;
    }
}

/**
 * Database methods / operations.
 */

/**
 * Get objects by id.
 * @param id    ->  Search parameter.
 * @return      ->  The object that belongs to the id.
 */
public String get(int id) 
{
    return ctDB.get(id);
}

/**
 * Adding objects to the database.
 * @param id -> The key reference to the object as 'id'.
 * @param object -> The object to cache.
 */
public void put(int id, String object)
{
    ctDB.put(id, object);

    db.commit();
}
}

然后做:

 DBManager manager = DBManager.getInstance();
 manager.put(1, "test");
 Sytem.out.println(manger.get(1));

【讨论】:

    【解决方案3】:

    如果您为大多数参数设置默认值,G1GC 会很好地工作。只设置关键参数

    -XX:MaxGCPauseMillis
    -XX:G1HeapRegionSize
    -XX:ParallelGCThreads
    -XX:ConcGCThreads
    

    其他一切都交给 Java。

    您可以在以下帖子中找到更多详细信息:

    Java 7 (JDK 7) garbage collection and documentation on G1

    Why do I get OutOfMemory when 20% of the heap is still free?

    使用诸如mat 之类的内存分析工具来了解根本原因。

    在您的情况下,很明显 oldgen 正在增长。检查可能的内存泄漏。如果您没有发现内存泄漏,请进一步增加堆内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-09
      • 2015-08-19
      相关资源
      最近更新 更多