【发布时间】:2012-01-18 05:33:30
【问题描述】:
编辑 #2 - 我认为这可能是过度优化,并会尝试按原样使用 Robot 类,直到遇到问题,但 FWIW:
我正在设计一个应用程序(仅限于 OSX Lion),它与一些 CPU 和内存密集型软件一起运行,包括不应因 CPU 峰值/过载而停止运行的音频软件。像大多数音频软件一样,内存不断被读/写。
这是一个非常轻量级的应用程序 - 它的主要目的是使用 Robot 类的 createScreenCapture 方法在长达几个小时的过程中每分钟截取几张屏幕截图。
在随后的每个屏幕截图中,都不需要前一个屏幕截图。看了下Robot源码,好像每次调用createScreenCapture()都会创建一个新的BufferedImage,也就是说前一个是留给垃圾回收的,对吧?
我是 Java 和垃圾收集器的新手。我担心由于 GC 管理/删除这些旧捕获的必要性而导致的 CPU 峰值。理想情况下,图像可以直接从屏幕上每次覆盖内存中的同一区域,并且应用程序的内存占用将保持相当稳定,而不会过多地进行 GC。
是否可以尝试扩展 Robot 类并覆盖 createScreenCapture() 以便它使用相同的静态 BufferedImage?如果这行得通,我看不出任何人都以当前的 Robot 为每次捕获分配新内存的方式有任何优势。
谢谢。
编辑 #1 - 取自 here 的相关机器人源代码如下。看来不仅每次都创建一个新的BufferedImage,而且同样大的int[]、DataBufferInt、WritableRaster。加上 peer.getRGBPixels() 产生的任何开销 - 这似乎连接到图形驱动程序,但我找不到任何来源或信息。我认为不重用相同的内存是浪费的,我错了吗?很明显,使用静态指针不会解决任何问题。 BufferedImage 需要使用它自己的 set 方法被覆盖。我为此添加了自己的 cmets,并用 $$$ 标记。从理论上讲,它会为我的应用程序节省大量 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;
}
【问题讨论】:
-
可能不是您正在等待的答案,但我真的建议您尝试一下。我的意思是,使用默认实现编写您的应用程序,然后对其进行性能测试。只有当 CPU 出现问题时,才考虑如何解决它们。如果万恶之源,过早的优化......
-
编辑我的问题后,我意识到你是对的。我将按原样使用它,看看会发生什么。另外,我使用的是全新的 MacBook……虽然我知道理论上它对于涉及重复屏幕捕获的应用程序来说是一种浪费。
-
在了解了有关扩展和覆盖的更多信息后,我无法做到我所追求的,因为所需的方法 peer.getRGBPixels() 是私有的。不过,我很惊讶没有人考虑编写更有效的屏幕捕获操作,直接重用相同的内存分配。
标签: java memory-management garbage-collection screen-capture awtrobot