2

我们有一个内存泄漏问题,我们不知道某个类的太多实例是从什么/从哪里创建/引用的。这发生在生产中的重负载下,我们无法获取堆转储(使用堆转储会使 HA 服务器挂起太久)。由于性能下降,运行时分析也不是生产站点上的一个选项,客户对随机崩溃更满意,而不是在监控期间试图捕捉崩溃瞬间时感到痛苦。我们不知道如何启动崩溃(泄漏),它只是在某些时候发生。

有没有办法在运行时从应用程序本身获取对象引用/实例化点?

我查看了http://docs.oracle.com/javase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/ObjectReference.html,它给出了这样的想法是可能的。

任何指针如何在没有堆转储方式的情况下最好使用自定义代码来实现这一点?已尝试在测试环境中重现该问题,并且似乎是穷尽的野鹅追逐。我们现在想要一种蛮力的方法来找到原因。

4

2 回答 2

2

建议您尝试检查导致此类泄漏的代码。这里有一些关于相同的教程和帮助

IBM 关于处理 Java 中的内存泄漏的文章 http://www.ibm.com/developerworks/library/j-leaks/

其他一些有用的文章 http://www.openlogic.com/wazi/bid/188158/How-to-Fix-Memory-Leaks-in-Java

还有一个Eclipse 内存分析器工具

但最推荐的解决方案是尝试jvisualvm在与您的程序运行相同的机器上从 JVM 运行并启用分析。

于 2012-10-31T12:00:17.540 回答
0

我们通过在实例化和克隆上收集堆栈跟踪来解决这个问题。当内存不足时,我们将它们转储到调度程序上。

我们知道导致问题的 Object 类,只需要寻找它的诞生地:

@EntityListeners(AbstractDTOJpaEventListener.class)
@MappedSuperclass
public abstract class AbstractDTO implements Storeable, Serializable, Cloneable {
  /** */
  private static final String SHADOWED_CLASS = "Custom";
  /** */
  protected final static boolean DEBUG_CUSTOM_INSTANCES = true;
  /** */
  public static long TARGET_HITRATE_PER_INTERVAL = 400000;
  /** */
  public static long LOGGING_INTERVAL = Times.MILLISECONDS_IN_TEN_SECONDS;
  /** */
  private static long previousLoggingTime;
  /** */
  protected static int hits;
  /** */
  protected static boolean hitting;
  /** */
  protected static int hitsWithinInterval;

  /**
   * @author Martin
   */
  public static class Hi {
    /**
     * 
     */
    private long hitted;
    private final long createdAt;
    private final StackTraceElement[] stackTraceElements;
    private final String threadName;

    /**
     * @param threadName 
     * @param stackTraceElements 
     */
    public Hi(String threadName, StackTraceElement[] stackTraceElements) {
      this.threadName = threadName;
      this.createdAt = System.currentTimeMillis();
      this.stackTraceElements = stackTraceElements;
    }

    /**
     *
     */
    public void hit() {
      hitted++;
    }

    /**
     * @return the hitted
     */
    public long getHitted() {
      return hitted;
    }

    /**
     * @param hitted the hitted to set
     */
    public void setHitted(long hitted) {
      this.hitted = hitted;
    }

    /**
     * @return the createdAt
     */
    public long getCreatedAt() {
      return createdAt;
    }

    /**
     * @return the stackTraceElements
     */
    public StackTraceElement[] getStackTraceElements() {
      return stackTraceElements;
    }

    /**
     * @return the threadName
     */
    public String getThreadName() {
      return threadName;
    }
  }

  /** */
  protected final static Map<String, Hi> INSTANCE_SHADOW = new ConcurrentHashMap<String, Hi>();

  private static final Comparator<? super Entry<String, Hi>> COMPARATOR = new Comparator<Entry<String, Hi>>() {
    @Override
    public int compare(Entry<String, Hi> o1, Entry<String, Hi> o2) {
      if (o1 == o2) {
        return 0;
      }

      return -Utils.compareNullSafe(o1.getValue().getHitted(), o2.getValue().getHitted(), Compare.ARG0_FIRST);
    }
  };

  /**
   * @param <T> 
   * @return T
   * @see java.lang.Object#clone()
   */
  @SuppressWarnings("unchecked")
  public <T extends AbstractDTO> T clone() {
    try {
      return (T) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    } finally {
      if (DEBUG_CUSTOM_INSTANCES && getClass().getSimpleName().equals(SHADOWED_CLASS)) {
        shadowInstance();
      }
    }
  }

  /**
   * 
   */
  protected void shadowInstance() {
    if (DEBUG_CUSTOM_INSTANCES) {
      final long currentTimeMillis = System.currentTimeMillis();

      if (TARGET_HITRATE_PER_INTERVAL <= ++hitsWithinInterval) {
        hitting = true;
      }

      if ((TARGET_HITRATE_PER_INTERVAL / 2) <= ++hits) {
        final Thread currentThread = Thread.currentThread();

        final StackTraceElement[] stackTrace = currentThread.getStackTrace();

        final String key = Utils.getPropertyPath(String.valueOf(System.identityHashCode(currentThread)), displayStackLocaktion(stackTrace))
            .intern();

        Hi hi = INSTANCE_SHADOW.get(key);

        if (hi == null) {
          synchronized (key) {
            hi = INSTANCE_SHADOW.get(key);

            if (hi == null) {
              INSTANCE_SHADOW.put(key, hi = new Hi(currentThread.getName(), stackTrace));
            }

          }
        }

        hi.hit();
      }

      {
        if (getLoggingInterval(currentTimeMillis) != getLoggingInterval(previousLoggingTime)) {
          if (hitsWithinInterval < TARGET_HITRATE_PER_INTERVAL) {
            if (hitting) {
              hitting = false;
            } else {
              hits = 0; // Reset measuring on second round, give chance to burtsy hits
            }
          }

          hitsWithinInterval = 0;
          previousLoggingTime = currentTimeMillis;
        }
      }
    }
  }

  /**
   * @param time
   * @return long
   */
  private long getLoggingInterval(long time) {
    return time / LOGGING_INTERVAL;
  }

  /**
   * @return String
   */
  public static String toStringShadows() {
    final ArrayList<Entry<String, Hi>> entries;

    synchronized (INSTANCE_SHADOW) {
      entries = Convert.toMinimumArrayList(INSTANCE_SHADOW.entrySet());
      INSTANCE_SHADOW.clear();
    }

    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(new Timestamp(System.currentTimeMillis()) + " " + SHADOWED_CLASS + " Class instance instantiantion summary:\n");
    stringBuilder.append("hits=" + hits + ", hitting=" + hitting + ", hitsWithinInterval=" + hitsWithinInterval + ", previousLoggingTime=" + new java.sql.Timestamp(previousLoggingTime));

    if (entries.isEmpty()) {
      return stringBuilder.toString();
    }

    Collections.sort(entries, COMPARATOR);

    int index = 0;
    stringBuilder.append("-----------------------------------------------------------------------");

    for (Entry<String, Hi> entry : entries) {
      Utils.append(stringBuilder, entry.getValue().getHitted() + "\t" + entry.getKey(), "\n");
    }

    for (Entry<String, Hi> entry : entries) {
      final Hi hi = entry.getValue();
      final StackTraceElement[] stackTrace = hi.getStackTraceElements();
      final String groupName = entry.getKey();

      final String threadName = hi.getThreadName();

      stringBuilder.append("\n").append(++index).append('\t');
      stringBuilder.append(hi.getHitted()).append("\tpcs\t").append(groupName);
      stringBuilder.append("\t").append(new Timestamp(hi.getCreatedAt()).toString()).append('\t').append(threadName)
          .append('\t').append(Convert.toString(stackTrace));
    }

    return stringBuilder.toString();
  }

  /**
   * @param stackTrace
   * @return String
   */
  private static String displayStackLocaktion(final StackTraceElement[] stackTrace) {
    StackTraceElement firstDistinguishingStackTraceElement = null;

    for (int index = 0; index < stackTrace.length; index++) {
      firstDistinguishingStackTraceElement = stackTrace[index];
      if (!Arrays.asList(UNWANTED_LOCATIONS).contains(firstDistinguishingStackTraceElement.getClassName())) {
        break;
      }
    }

    StackTraceElement lastDistinguishingStackTraceElement = null;

    for (int index = stackTrace.length-1; 0 <= index; index--) {
      lastDistinguishingStackTraceElement = stackTrace[index];
      if (lastDistinguishingStackTraceElement.getClassName().startsWith(OUR_PACKAGE_DOMAIN)) {
        break;
      }
    }

    return Utils.getPropertyPath(displayName(firstDistinguishingStackTraceElement) + "<-"
        + displayName(lastDistinguishingStackTraceElement));
  }

  /**
   * @param firstDistinguishingStackTraceElement
   * @return String
   */
  private static String displayName(StackTraceElement firstDistinguishingStackTraceElement) {
    return Utils.getPropertyPath(firstDistinguishingStackTraceElement.getClassName(), firstDistinguishingStackTraceElement.getMethodName(),
        String.valueOf(firstDistinguishingStackTraceElement.getLineNumber()));
  }
}
于 2012-11-05T07:49:00.543 回答