Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
package com.taobao.arthas.core.command.express;

import java.lang.ref.WeakReference;

/**
* ExpressFactory
* @author ralf0131 2017-01-04 14:40.
* @author hengyunabc 2018-10-08
*/
public class ExpressFactory {

private static final ThreadLocal<Express> expressRef = new ThreadLocal<Express>() {
@Override
protected Express initialValue() {
return new OgnlExpress();
}
};
/**
* 这里不能直接在 ThreadLocalMap 里强引用 Express(它由 ArthasClassLoader 加载),否则 stop/detach 后会被业务线程持有,
* 导致 ArthasClassLoader 无法被 GC 回收。
*
* 用 WeakReference 打断强引用链:Thread -> ThreadLocalMap -> value(WeakReference) -X-> Express。
*/
private static final ThreadLocal<WeakReference<Express>> expressRef = ThreadLocal
.withInitial(() -> new WeakReference<Express>(new OgnlExpress()));

/**
* get ThreadLocal Express Object
* @param object
* @return
*/
public static Express threadLocalExpress(Object object) {
return expressRef.get().reset().bind(object);
WeakReference<Express> reference = expressRef.get();
Express express = reference == null ? null : reference.get();
if (express == null) {
express = new OgnlExpress();
expressRef.set(new WeakReference<Express>(express));
}
return express.reset().bind(object);
}

public static Express unpooledExpress(ClassLoader classloader) {
Expand All @@ -29,4 +39,4 @@ public static Express unpooledExpress(ClassLoader classloader) {
}
return new OgnlExpress(new ClassLoaderClassResolver(classloader));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,22 @@
*/
public class TimeTunnelAdviceListener extends AdviceListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(TimeTunnelAdviceListener.class);
private final ThreadLocal<ObjectStack> argsRef = new ThreadLocal<ObjectStack>() {
@Override
protected ObjectStack initialValue() {
return new ObjectStack(512);
}
};
/**
* 用 JDK 的 Object[] 做一个固定大小的 ring stack(只存业务对象),避免把 ArthasClassLoader 加载的 ObjectStack 放进
* 业务线程的 ThreadLocalMap 里,导致 stop/detach 后 ArthasClassLoader 无法被 GC 回收。
*
* <pre>
* 约定:
* - store[0] 存储 int[1] 的 pos(0..cap)
* - store[1..cap] 存储 args(Object[])
* </pre>
*/
private static final int ARGS_STACK_SIZE = 512;
private final ThreadLocal<Object[]> argsRef = ThreadLocal.withInitial(() -> {
Object[] store = new Object[ARGS_STACK_SIZE + 1];
store[0] = new int[1];
return store;
});

private TimeTunnelCommand command;
private CommandProcess process;
Expand All @@ -46,26 +56,62 @@ public TimeTunnelAdviceListener(TimeTunnelCommand command, CommandProcess proces
@Override
public void before(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args)
throws Throwable {
argsRef.get().push(args);
pushArgs(args);
threadLocalWatch.start();
}

@Override
public void afterReturning(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
Object returnObject) throws Throwable {
//取出入参时的 args,因为在函数执行过程中 args可能被修改
args = (Object[]) argsRef.get().pop();
args = popArgs();
afterFinishing(Advice.newForAfterReturning(loader, clazz, method, target, args, returnObject));
}

@Override
public void afterThrowing(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
Throwable throwable) {
//取出入参时的 args,因为在函数执行过程中 args可能被修改
args = (Object[]) argsRef.get().pop();
args = popArgs();
afterFinishing(Advice.newForAfterThrowing(loader, clazz, method, target, args, throwable));
}

private void pushArgs(Object[] args) {
Object[] store = argsRef.get();
int[] posHolder = (int[]) store[0];

int cap = store.length - 1;
int pos = posHolder[0];
if (pos < cap) {
pos++;
} else {
// if stack is full, reset pos
pos = 1;
}
store[pos] = args;
posHolder[0] = pos;
}

private Object[] popArgs() {
Object[] store = argsRef.get();
int[] posHolder = (int[]) store[0];

int cap = store.length - 1;
int pos = posHolder[0];
if (pos > 0) {
Object[] args = (Object[]) store[pos];
store[pos] = null;
posHolder[0] = pos - 1;
return args;
}

pos = cap;
Object[] args = (Object[]) store[pos];
store[pos] = null;
posHolder[0] = pos - 1;
return args;
}

private void afterFinishing(Advice advice) {
double cost = threadLocalWatch.costInMillis();
TimeFragment timeTunnel = new TimeFragment(advice, LocalDateTime.now(), cost);
Expand Down Expand Up @@ -103,57 +149,4 @@ private void afterFinishing(Advice advice) {
abortProcess(process, command.getNumberOfLimit());
}
}

/**
*
* <pre>
* 一个特殊的stack,为了追求效率,避免扩容。
* 因为这个stack的push/pop 并不一定成对调用,比如可能push执行了,但是后面的流程被中断了,pop没有被执行。
* 如果不固定大小,一直增长的话,极端情况下可能应用有内存问题。
* 如果到达容量,pos会重置,循环存储数据。所以使用这个Stack如果在极端情况下统计的数据会不准确,只用于monitor/watch等命令的计时。
*
* </pre>
*
* @author hengyunabc 2020-05-20
*
*/
static class ObjectStack {
private Object[] array;
private int pos = 0;
private int cap;

public ObjectStack(int maxSize) {
array = new Object[maxSize];
cap = array.length;
}

public int size() {
return pos;
}

public void push(Object value) {
if (pos < cap) {
array[pos++] = value;
} else {
// if array is full, reset pos
pos = 0;
array[pos++] = value;
}
}

public Object pop() {
if (pos > 0) {
pos--;
Object object = array[pos];
array[pos] = null;
return object;
} else {
pos = cap;
pos--;
Object object = array[pos];
array[pos] = null;
return object;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ public String apply(String data) {
term.write(data);
return data;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,31 @@
*/
public class ThreadLocalWatch {

private final ThreadLocal<LongStack> timestampRef = new ThreadLocal<LongStack>() {
@Override
protected LongStack initialValue() {
return new LongStack(1024 * 4);
}
};
/**
* 用 long[] 做一个固定大小的 ring stack,避免把 ArthasClassLoader 加载的对象塞到业务线程的 ThreadLocalMap 里,
* 从而在 stop/detach 后导致 ArthasClassLoader 无法被 GC 回收。
*
* <pre>
* 约定:
* - stack[0] 存储当前 pos(0..cap)
* - stack[1..cap] 存储数据
* </pre>
*/
private static final int DEFAULT_STACK_SIZE = 1024 * 4;
private final ThreadLocal<long[]> timestampRef = ThreadLocal.withInitial(() -> new long[DEFAULT_STACK_SIZE + 1]);

public long start() {
final long timestamp = System.nanoTime();
timestampRef.get().push(timestamp);
push(timestampRef.get(), timestamp);
return timestamp;
}

public long cost() {
return (System.nanoTime() - timestampRef.get().pop());
return (System.nanoTime() - pop(timestampRef.get()));
}

public double costInMillis() {
return (System.nanoTime() - timestampRef.get().pop()) / 1000000.0;
return (System.nanoTime() - pop(timestampRef.get())) / 1000000.0;
}

/**
Expand All @@ -42,39 +48,31 @@ public double costInMillis() {
* @author hengyunabc 2019-11-20
*
*/
static class LongStack {
private long[] array;
private int pos = 0;
private int cap;

public LongStack(int maxSize) {
array = new long[maxSize];
cap = array.length;
}

public int size() {
return pos;
static void push(long[] stack, long value) {
int cap = stack.length - 1;
int pos = (int) stack[0];
if (pos < cap) {
pos++;
} else {
// if stack is full, reset pos
pos = 1;
}
stack[pos] = value;
stack[0] = pos;
}

public void push(long value) {
if (pos < cap) {
array[pos++] = value;
} else {
// if array is full, reset pos
pos = 0;
array[pos++] = value;
}
static long pop(long[] stack) {
int cap = stack.length - 1;
int pos = (int) stack[0];
if (pos > 0) {
long value = stack[pos];
stack[0] = pos - 1;
return value;
}

public long pop() {
if (pos > 0) {
pos--;
return array[pos];
} else {
pos = cap;
pos--;
return array[pos];
}
}
pos = cap;
long value = stack[pos];
stack[0] = pos - 1;
return value;
}
}
}
Loading
Loading