【问题标题】:How to find all referrers of an object in Java?如何在 Java 中查找对象的所有引用者?
【发布时间】:2012-10-31 11:54:43
【问题描述】:

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

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

我查看了http://docs.oracle.com/javase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/ObjectReference.html,并认为这样的事情是可能的。

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

【问题讨论】:

  • 与其尝试解决堆转储耗时过长的问题,不如尝试集中精力在测试环境中重现问题。查看 JMeter 之类的工具来扩大负载。
  • 已尝试在测试环境中重现该问题,并且似乎很详尽。我们现在需要一种蛮力方法来找到原因。
  • 我从来没有做过这样的事情,也许这是一个愚蠢的想法,但它是否可以作为记录客户端请求/应用程序操作的选项,以便以后在测试站点上进行复制?
  • 嗯.. 日志记录请求听起来也像是大海捞针 x 野鹅追逐。我们目前正在考虑在实例化和克隆时收集堆栈跟踪......并在内存不足时转储它们。我们知道导致问题的 Object 类,只需要寻找它的诞生地。

标签: java memory-leaks jvm


【解决方案1】:

建议您尝试检查导致此类泄漏的代码。以下是一些相关的教程和帮助

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

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

还有一个Eclipse Memory Analyser Tool

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

【讨论】:

  • 运行时分析也不是生产站点上的一个选项,因为性能下降,客户对随机崩溃更满意,而不是在监控期间试图捕捉崩溃瞬间的缓慢而痛苦。我们不知道如何启动崩溃(泄漏),它只是在某些时候发生。
  • 您永远不应该尝试在现场制作系统上进行分析。而是尝试在将其部署到测试系统之前进行分析并修复泄漏问题。
  • 我们不知道如何在测试环境中重现该问题,否则我们早就修复了。
  • 我能理解你的情况......你想要一些可以通过应用程序服务器放置的标志,这将执行更好的垃圾收集......为此,我找到了一个关于相同 @987654325 的 sun 演示文稿@ 但它只讨论了一些基本标志.. 我希望你一定已经申请了。
  • 我们目前正在考虑在实例化和克隆时收集堆栈跟踪,并在内存不足时转储它们。
【解决方案2】:

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

我们知道导致问题的 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()));
  }
}

【讨论】:

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