Runnable 和 ScheduledExecutorService 的内存泄漏
Memory leak with Runnable and ScheduledExecutorService
我正在制作这个 status/menu 酒吧应用程序,它在 Mac OS X 的状态栏中显示当前正在播放的歌曲。要从 Spotify 获取播放器状态,我必须创建并执行 AppleScript 并从中获取输出。然后使用 Graphics2D 中的 drawString() 绘制结果,将其设置到 BufferedImage 上,然后将其设置为托盘图标。
整个代码是 4 类 并且易于理解,可在此处获取:https://github.com/ZinoKader/Menify
现在进入问题
我的可运行程序似乎以前所未有的方式消耗内存。应用程序每秒多使用 2-3MB 的 RAM,如果我不去管它的话,会达到千兆字节。到目前为止,我尝试过的是刷新并处理我所有的图像和 Graphics2D 资源,刷新并关闭每个输入流、输出流并销毁我在 AppleScripthHelper 中创建的 Process 对象。
即使是这样,只要调用一个静态方法,RAM 就会很快堆积起来。
final Runnable refreshPlayingText = () -> {
AppleScriptHelper.evalAppleScript(ScriptConstants.SPOTIFY_META_DATA_SCRIPT);
}
//update every 50ms
mainExecutor.scheduleAtFixedRate(refreshPlayingText, 0, 50, TimeUnit.MILLISECONDS);
和 AppleScriptHelper
class AppleScriptHelper {
private static final int EOF = -1;
static String evalAppleScript(String code) {
String[] args = { "osascript", "-e", code };
try {
Process process = Runtime.getRuntime().exec(args);
process.waitFor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bigByteArray = new byte[4096];
InputStream is = process.getInputStream();
copyLargeStream(is, baos, bigByteArray); //write to outputstream
String result = baos.toString().trim();
is.close();
baos.flush();
baos.close();
process.destroyForcibly();
return result;
} catch (IOException | InterruptedException e) {
Log.debug(e);
return null;
}
}
private static void copyLargeStream(InputStream input, OutputStream output, byte[] buffer) throws IOException {
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
input.close();
output.close();
}
}
所以问题是,是什么占用了所有 RAM?为什么似乎什么都没有被垃圾收集?
您遇到的不是内存泄漏!
根据 Java™ 进程和线程教程 (https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html),
A process generally has a complete, private set of basic run-time resources; in particular, each process has its own memory space.
您每 50 毫秒创建一个新进程,这很可能会占用您的可用内存。
创建太多进程会导致 thrashing
,您会注意到 CPU 性能下降。根据进程的作用,很可能有更有效的方法来实现您的目标,而无需每秒创建 20 个进程。
我正在制作这个 status/menu 酒吧应用程序,它在 Mac OS X 的状态栏中显示当前正在播放的歌曲。要从 Spotify 获取播放器状态,我必须创建并执行 AppleScript 并从中获取输出。然后使用 Graphics2D 中的 drawString() 绘制结果,将其设置到 BufferedImage 上,然后将其设置为托盘图标。
整个代码是 4 类 并且易于理解,可在此处获取:https://github.com/ZinoKader/Menify
现在进入问题
我的可运行程序似乎以前所未有的方式消耗内存。应用程序每秒多使用 2-3MB 的 RAM,如果我不去管它的话,会达到千兆字节。到目前为止,我尝试过的是刷新并处理我所有的图像和 Graphics2D 资源,刷新并关闭每个输入流、输出流并销毁我在 AppleScripthHelper 中创建的 Process 对象。
即使是这样,只要调用一个静态方法,RAM 就会很快堆积起来。
final Runnable refreshPlayingText = () -> {
AppleScriptHelper.evalAppleScript(ScriptConstants.SPOTIFY_META_DATA_SCRIPT);
}
//update every 50ms
mainExecutor.scheduleAtFixedRate(refreshPlayingText, 0, 50, TimeUnit.MILLISECONDS);
和 AppleScriptHelper
class AppleScriptHelper {
private static final int EOF = -1;
static String evalAppleScript(String code) {
String[] args = { "osascript", "-e", code };
try {
Process process = Runtime.getRuntime().exec(args);
process.waitFor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bigByteArray = new byte[4096];
InputStream is = process.getInputStream();
copyLargeStream(is, baos, bigByteArray); //write to outputstream
String result = baos.toString().trim();
is.close();
baos.flush();
baos.close();
process.destroyForcibly();
return result;
} catch (IOException | InterruptedException e) {
Log.debug(e);
return null;
}
}
private static void copyLargeStream(InputStream input, OutputStream output, byte[] buffer) throws IOException {
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
input.close();
output.close();
}
}
所以问题是,是什么占用了所有 RAM?为什么似乎什么都没有被垃圾收集?
您遇到的不是内存泄漏!
根据 Java™ 进程和线程教程 (https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html),
A process generally has a complete, private set of basic run-time resources; in particular, each process has its own memory space.
您每 50 毫秒创建一个新进程,这很可能会占用您的可用内存。
创建太多进程会导致 thrashing
,您会注意到 CPU 性能下降。根据进程的作用,很可能有更有效的方法来实现您的目标,而无需每秒创建 20 个进程。