如何在Java中找到对象的所有引用者?
我们有一个内存泄漏问题,我们不知道某个类的太多实例是从什么地方创建的。这种情况发生在生产负载较重的情况下,我们无法获得堆转储(获取堆转储会使HA服务器挂起太长时间)。在生产站点上,运行时评测也不是一个选项,因为性能下降,客户更喜欢随机崩溃,而不是在试图寻找崩溃瞬间的监控过程中痛苦地缓慢。我们不知道如何启动崩溃(泄漏),它只是在某些时候发生 有没有一种方法可以在运行时从应用程序本身中获取对象引用器/实例化点 我看了一下,它给出了一个想法,像这样的事情是可能的如何在Java中找到对象的所有引用者?,java,memory-leaks,jvm,Java,Memory Leaks,Jvm,我们有一个内存泄漏问题,我们不知道某个类的太多实例是从什么地方创建的。这种情况发生在生产负载较重的情况下,我们无法获得堆转储(获取堆转储会使HA服务器挂起太长时间)。在生产站点上,运行时评测也不是一个选项,因为性能下降,客户更喜欢随机崩溃,而不是在试图寻找崩溃瞬间的监控过程中痛苦地缓慢。我们不知道如何启动崩溃(泄漏),它只是在某些时候发生 有没有一种方法可以在运行时从应用程序本身中获取对象引用器/实例化点 我看了一下,它给出了一个想法,像这样的事情是可能的 有没有任何指针可以指示如何更好地使用自
有没有任何指针可以指示如何更好地使用自定义代码而不使用堆转储方式来实现这一点?在测试环境中重现这个问题已经尝试过了,似乎是彻底的白费力气。我们现在需要一种暴力的方式来找到原因 建议您尝试检查导致此类泄漏的代码。这里有一些教程和帮助 IBM关于在Java中处理内存泄漏的文章 其他一些有用的文章 还有一个 但最推荐的解决方案是
尝试在程序运行的同一台机器上从JVM运行,并启用评测功能。我们通过在实例化和克隆时收集堆栈跟踪来解决此问题。。当内存不足时,将它们转储到调度程序中 我们知道导致问题的对象类,只需要找出它的出生地:
@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()));
}
}
@EntityListeners(AbstractdTojPaeEventListener.class)
@映射超类
公共抽象类AbstractDTO实现可存储、可序列化、可克隆{
/** */
私有静态最终字符串阴影_CLASS=“自定义”;
/** */
受保护的最终静态布尔调试\自定义\实例=true;
/** */
公共静态长目标命中率每间隔=400000;
/** */
公共静态长日志记录间隔=Times.millizes\u IN\u TEN\u SECONDS;
/** */
私有静态日志记录时间长;
/** */
受保护的静态int命中;
/** */
保护静态布尔打击;
/** */
防静电int-HITSWITHINERVAL;
/**
*@作者马丁
*/
公共静态类Hi{
/**
*
*/
私人长打;
私人最终长创建数据;
私有最终StackTraceElement[]StackTraceElement;
私有最终字符串threadName;
/**
*@param threadName
*@param stackTraceElements
*/
公共Hi(字符串threadName,StackTraceElement[]stackTraceElements){
this.threadName=threadName;
this.createdAt=System.currentTimeMillis();
this.stackTraceeElements=stackTraceeElements;
}
/**
*
*/
公开无效点击(){
hitted++;
}
/**
*@returnthehitted
*/
公共长挂接(){
回击;
}
/**
*@param击中了被击中的人
*/
公共无效设置命中(长命中){
this.hitted=hitted;
}
/**
*@returnthecreatedat
*/
公共长getCreatedAt(){
返回createdAt;
}
/**
*@返回stackTraceElements
*/
公共StackTraceElement[]getStackTraceElements(){
返回堆栈跟踪元素;
}
/**
*@返回threadName
*/
公共字符串getThreadName(){
返回threadName;
}
}
/** */
受保护的最终静态映射实例_SHADOW=new ConcurrentHashMap();
private static final Comparator不要试图在堆转储上花费太长时间,试着集中精力在测试环境中重现问题。看看像JMeter这样的工具,可以放大负载。在测试环境中重现问题已经尝试过了,而且似乎是详尽无遗的。我们现在需要一种蛮力方法来找到原因是他。我从来没有做过这样的事情,也许这是一个愚蠢的想法,但它是否可以作为一个选项来记录客户端请求/应用程序操作,以便以后在测试站点上进行复制?嗯……记录请求听起来也像是大海捞针x野鹅。我们目前正在考虑收集实例化和克隆时的堆栈跟踪…并在内存不足时将其转储。我们知道导致问题的对象类,只需要查找它的出生地。由于性能下降,在生产站点上也不选择运行时评测,客户更喜欢随机崩溃,而不是在试图在中查找崩溃的监视过程中痛苦地缓慢斯坦特,我们不知道如何启动坠机(泄漏),它只是在某些时候发生。您不应该尝试在实时生产系统上进行配置文件。相反,在将其部署到测试系统上之前尝试进行配置文件,并修复泄漏问题。我们不知道如何在测试环境中重现该问题,否则我们早就解决了。我可以理解您的情况…您需要一些标志来解决此问题吗至少可以通过应用服务器进行垃圾收集,这将更好地执行垃圾收集…为此,我找到了一个sun演示文稿,但它只讨论了一些基本标志..我希望您已经应用了这些标志。我们目前正在考虑收集实例化和克隆上的StackTrace..并在memory变低。通过Java代理也存在一种本机方法: