编辑#2 - 我认为这可能是过度优化,并且会尝试按原样使用 Robot 类,直到遇到问题,但 FWIW:
我正在设计一个应用程序(严格用于 OSX Lion),它与一些 CPU 和内存密集型软件一起运行,包括不应因 CPU 峰值/过载而停止运行的音频软件。像大多数音频软件一样,内存不断被读/写。
createScreenCapture
这是一个非常轻量级的应用程序 - 它的主要目的是使用Robot 类的方法在长达几个小时的过程中每分钟捕获一些屏幕截图。
在每个后续的屏幕截图中,不需要前一个屏幕截图。查看Robot源代码后,似乎BufferedImage
每次createScreenCapture()
调用都会创建一个新的,这意味着前一个是留给垃圾收集的,对吗?
我是 Java 和垃圾收集器的新手。我担心由于 GC 管理/删除这些旧捕获的必要性而导致的 CPU 峰值。理想情况下,图像可以直接从屏幕上每次覆盖内存中的相同区域,并且应用程序的内存占用将保持相当稳定,而 GC 不会工作太多。
尝试扩展 Robot 类并覆盖createScreenCapture()
以使其使用相同的静态 BufferedImage 是否可取/可行?如果这行得通,我看不到任何人都以机器人为每次捕获分配新内存的当前方式有任何优势。
谢谢。
编辑#1 - 取自此处的相关机器人源代码如下。看来不仅每次都创建一个新的BufferedImage,而且同样大的int[]、DataBufferInt、WritableRaster。加上 peer.getRGBPixels() 产生的任何开销 - 这似乎连接到图形驱动程序,但我找不到任何来源或信息。我认为不重用相同的内存是浪费的,我错了吗?很明显,使用静态指针不会解决任何问题。BufferedImage 需要使用它自己的 set 方法被覆盖。我对此方法添加了自己的评论,并用$$ 标记。从理论上讲,它会为我的应用程序节省大量 GC 操作,但这是否值得担心?如果我对 java 和 profiling 有更多的了解,我会尝试一切。
public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
checkScreenCaptureAllowed();
// according to the spec, screenRect is relative to robot's GD
Rectangle translatedRect = new Rectangle(screenRect);
translatedRect.translate(gdLoc.x, gdLoc.y);
checkValidRect(translatedRect);
BufferedImage image;
DataBufferInt buffer;
WritableRaster raster;
if (screenCapCM == null) {
/*
* Fix for 4285201
* Create a DirectColorModel equivalent to the default RGB ColorModel,
* except with no Alpha component.
*/
screenCapCM = new DirectColorModel(24,
/* red mask */ 0x00FF0000,
/* green mask */ 0x0000FF00,
/* blue mask */ 0x000000FF);
}
// need to sync the toolkit prior to grabbing the pixels since in some
// cases rendering to the screen may be delayed
Toolkit.getDefaultToolkit().sync();
int pixels[]; //$$$ do away with this array altogether?
int[] bandmasks = new int[3];
pixels = peer.getRGBPixels(translatedRect); //$$$ not needed
buffer = new DataBufferInt(pixels, pixels.length); //$$$ not needed
bandmasks[0] = screenCapCM.getRedMask();
bandmasks[1] = screenCapCM.getGreenMask();
bandmasks[2] = screenCapCM.getBlueMask();
raster = Raster.createPackedRaster(buffer, translatedRect.width, translatedRect.height, translatedRect.width, bandmasks, null);
//$$$ not needed
//$$$ i don't know what bandmasks are, but maybe the full-size raster could be initialized once with the bandmasks array, then...
//$$$ ...use raster.setPixels(0, 0, translatedRect.width, translatedRect.height, peer.getRGBPixels(translatedRect));
image = new BufferedImage(screenCapCM, raster, false, null);
//$$$ instead use image.setData(raster);
CachingSurfaceManager.restoreLocalAcceleration(image);
return image;
}