System.out.println performance

本文探讨了使用System.out.println进行日志记录时可能遇到的性能问题,特别是当多线程环境中频繁调用此方法时可能导致系统性能显著下降。文章通过代码分析揭示了其内部同步机制如何成为性能瓶颈,并提供了实验数据说明不同场景下的性能退化情况。

Any system that logs vast amounts of information, needs to think about performance. The activity of logging cannot be a synchronous blocking call that returns only when the message has been logged to a persistence store. Enterprise logging systems usually make use of a message bus to carry messages asynchronously to their target persistence store. Be it a database or a file.

Talking about logging brings us to System.out.println() (Lets call is SOP for short). It is a surprisingly commonly method to “log” messages. SOP is not meant to be used as a logging system, but unfortunately there is no dearth of projects that have these statements scattered around the code base. The adverse effects that this statement can bring on the performance of the system is often not recognized as well as it should be.

Why is SOP a bottleneck for performance ? This is why…

Code excerpt from PrintStream.java:

private void write(String s) { try { synchronized (this) { ensureOpen(); textOut.write(s); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush && (s.indexOf('\n') >= 0)) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }

All SOP calls on a String result in a corresponding call to the private write(String s) method in PrintStream.java. The synchronized block ensures that every thread has to wait for this call to end before it can proceed. Whats more, the calls to flushBuffer() on the BufferedWriter that is textOut result in the execution of more synchronized blocks. These blocks are expensive to execute.

Here is a chart that shows how performance degrades when a program logs 100,000 messages through various threads. The rate of degradation also depends on the number of characters that are passing through the stream.

System.out.println performance:
System.out.println performance

The degradation experienced in ‘Web application X / Y’ may vary, but it cannot be discounted. Avoid using SOPs to log messages in your app. Even one or two that are left over can harm performance under the right conditions. Let us also not forget about e.printStackTrace() and other forms of writing to console output that follow the same synchronized pattern.

package com.dnf.main; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.nio.ByteBuffer; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.swing.SwingUtilities; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; import org.opencv.videoio.VideoCapture; import org.opencv.videoio.Videoio; import org.opencv.videoio.VideoWriter; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.image.ImageView; import javafx.scene.image.PixelFormat; import javafx.scene.image.WritableImage; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; public class MS2130FXCapture { // 核心资源 private static ImageView imageView; private static VideoCapture videoCapture; // 状态控制 private static final AtomicBoolean isRunning = new AtomicBoolean(false); private static final AtomicBoolean isFXInitialized = new AtomicBoolean(false); // 线程管理 private static Thread captureThread; private static ExecutorService frameProcessingPool; // 资源池控制 private static final int MAT_POOL_SIZE = 4; private static final BlockingQueue<Mat> matPool = new ArrayBlockingQueue<>(MAT_POOL_SIZE); // 显示资源 private static final AtomicReference<BufferedImage> bufImgRef = new AtomicReference<>(); private static final AtomicReference<WritableImage> fxImgRef = new AtomicReference<>(); private static volatile Mat displayMat; // 帧率控制 private static final int TARGET_FPS = 20; private static final long FRAME_INTERVAL_MS = 1000 / TARGET_FPS; private static long lastFrameTime = System.currentTimeMillis(); // 设备参数 - 固定为1920x1080 private static final int DEVICE_WIDTH = 1920; private static final int DEVICE_HEIGHT = 1080; // 性能监控 private static final AtomicInteger frameCounter = new AtomicInteger(0); private static final long PERFORMANCE_CHECK_INTERVAL = 5000; // 5秒检查一次性能 private static long lastPerformanceCheck = System.currentTimeMillis(); // CPU使用率监控 private static long lastCpuTime = System.nanoTime(); private static long lastCpuCheckTime = System.currentTimeMillis(); private static final long CPU_CHECK_INTERVAL = 3000; // 3秒检查一次CPU private static final double MAX_CPU_USAGE = 2.0; // 最大允许CPU使用率 // 内存监控 private static final long MAX_MEMORY_MB = 300; // 最大允许内存300MB private static final long MEMORY_CHECK_INTERVAL = 3000; // 3秒检查一次内存 /** * 启动视频采集 */ public static void startCapture(JFXPanel fxPanel) { System.out.println("[自动采集] 开始初始化采集"); // 确保停止之前的采集 stopCapture(); // 重置状态 isRunning.set(true); System.out.println("[状态] isRunning 设置为 true"); int deviceIndex = findValidDeviceIndex(); if (deviceIndex == -1) { System.err.println("[错误] 未找到MS2130设备"); return; } // 初始化Mat池并预热 initMatPool(); // 初始化JavaFX环境 if (!isFXInitialized.get()) { System.out.println("[初始化] 首次初始化JavaFX"); SwingUtilities.invokeLater(() -> { Platform.runLater(() -> { System.out.println("[JavaFX] 在JavaFX线程中初始化"); initFXCapture(fxPanel, deviceIndex); isFXInitialized.set(true); }); }); } else { System.out.println("[初始化] JavaFX已初始化,直接启动采集"); Platform.runLater(() -> initFXCapture(fxPanel, deviceIndex)); } // 初始化线程池 - 使用单线程处理帧 frameProcessingPool = Executors.newSingleThreadExecutor(); System.out.println("[线程池] 帧处理线程池已创建"); } /** * 初始化Mat对象池 */ private static void initMatPool() { synchronized (matPool) { matPool.clear(); for (int i = 0; i < MAT_POOL_SIZE; i++) { matPool.offer(new Mat()); } System.out.println("[资源池] Mat对象池已初始化,大小: " + MAT_POOL_SIZE); } } /** * 初始化JavaFX UI组件 */ private static void initFXCapture(JFXPanel fxPanel, int deviceIndex) { System.out.println("[UI] 初始化JavaFX UI组件"); imageView = new ImageView(); imageView.setPreserveRatio(true); // 保持宽高比 imageView.setSmooth(false); // 平滑缩放 imageView.setCache(false); // 禁用缓存避免内存问题 // 设置默认显示尺寸为800x450 (16:9) imageView.setFitWidth(640); imageView.setFitHeight(360); StackPane root = new StackPane(imageView); root.setBackground( new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, javafx.geometry.Insets.EMPTY))); fxPanel.setScene(new Scene(root)); System.out.println("[UI] JavaFX场景已设置"); openDeviceAndStartCapture(deviceIndex); } /** * 枚举有效设备 */ private static int findValidDeviceIndex() { System.out.println("[设备检测] 开始检测可用设备..."); for (int i = 0; i < 4; i++) { VideoCapture testCapture = null; try { System.out.println("[设备检测] 尝试索引: " + i); // 尝试使用DSHOW驱动 testCapture = new VideoCapture(i, Videoio.CAP_DSHOW); if (!testCapture.isOpened()) { System.out.println("[设备检测] DSHOW驱动打开失败,尝试MSMF"); testCapture.release(); testCapture = new VideoCapture(i, Videoio.CAP_MSMF); } if (!testCapture.isOpened()) { System.out.println("[设备检测] 设备 " + i + " 未打开"); continue; } Mat testFrame = new Mat(); if (testCapture.read(testFrame) && !testFrame.empty()) { testFrame.release(); testCapture.release(); return i; } else { System.out.println("[设备检测] 设备 " + i + " 读取帧失败"); } testFrame.release(); } catch (Exception e) { System.err.println("设备检测错误: " + e.getMessage()); } finally { if (testCapture != null && testCapture.isOpened()) { testCapture.release(); } } } System.out.println("[设备检测] 未找到可用设备"); return -1; } /** * 打开设备并配置 */ private static void openDeviceAndStartCapture(int deviceIndex) { System.out.println("[设备] 尝试打开设备: " + deviceIndex); // 尝试多种驱动 videoCapture = new VideoCapture(deviceIndex, Videoio.CAP_DSHOW); if (!videoCapture.isOpened()) { System.out.println("[设备] DSHOW驱动打开失败,尝试MSMF"); videoCapture.open(deviceIndex, Videoio.CAP_MSMF); } if (!videoCapture.isOpened()) { System.err.println("[错误] 无法打开设备"); return; } // 设置设备分辨率为1920x1080 videoCapture.set(Videoio.CAP_PROP_FRAME_WIDTH, DEVICE_WIDTH); videoCapture.set(Videoio.CAP_PROP_FRAME_HEIGHT, DEVICE_HEIGHT); videoCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('M', 'J', 'P', 'G')); videoCapture.set(Videoio.CAP_PROP_BUFFERSIZE, 2); videoCapture.set(Videoio.CAP_PROP_FPS, TARGET_FPS); // 检查实际设置的分辨率 double actualWidth = videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH); double actualHeight = videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT); System.out.printf("[设备] 请求分辨率1920x1080,实际分辨率: %.0fx%.0f%n", actualWidth, actualHeight); if (actualWidth != DEVICE_WIDTH || actualHeight != DEVICE_HEIGHT) { System.err.println("[警告] 设备分辨率未设置为1920x1080,实际处理将以" + actualWidth + "x" + actualHeight + "进行"); } // 创建显示用的Mat displayMat = new Mat(DEVICE_HEIGHT, DEVICE_WIDTH, org.opencv.core.CvType.CV_8UC3); System.out.println("[资源] 显示Mat已创建"); // 启动采集线程 captureThread = new Thread(() -> captureLoop()); captureThread.setName("VideoCapture-Thread"); captureThread.setDaemon(true); captureThread.setPriority(Thread.MAX_PRIORITY - 1); // 适度优先级 captureThread.start(); System.out.println("[线程] 采集线程已启动"); } /** * 优化的采集循环 */ private static void captureLoop() { System.out.println("[采集线程] 开始运行"); System.out.println("[状态] isRunning = " + isRunning.get()); int consecutiveFailures = 0; long lastFpsTime = System.currentTimeMillis(); // 上次输出FPS的时间 int framesSinceLastReport = 0; // 上次报告后的帧数 while (isRunning.get()) { try { long currentTime = System.currentTimeMillis(); // 性能监控 - 每5秒检查一次 if (currentTime - lastPerformanceCheck > PERFORMANCE_CHECK_INTERVAL) { checkPerformance(); lastPerformanceCheck = currentTime; } // 帧率控制 long elapsed = currentTime - lastFrameTime; if (elapsed < FRAME_INTERVAL_MS) { Thread.sleep(1); continue; } // 获取空闲Mat Mat frameMat = matPool.poll(); if (frameMat == null) { System.out.println("[资源] Mat池为空,创建新Mat"); frameMat = new Mat(); } // 读取帧 boolean readSuccess = videoCapture.read(frameMat); if (!readSuccess || frameMat.empty()) { System.out.println("[警告] 读取帧失败: " + (frameMat.empty() ? "空帧" : "读取失败")); consecutiveFailures++; if (consecutiveFailures > 10) { System.err.println("[错误] 连续10次读取帧失败,停止采集"); break; } // 归还Mat并等待 if (frameMat != null && frameMat.cols() > 0) { matPool.offer(frameMat); } Thread.sleep(10); continue; } consecutiveFailures = 0; // 重置失败计数 // 更新计时器 lastFrameTime = currentTime; framesSinceLastReport++; // 提交处理任务 final Mat finalFrameMat = frameMat; frameProcessingPool.execute(() -> processFrame(finalFrameMat)); // 每秒输出一次FPS if (currentTime - lastFpsTime >= 1000) { double fps = framesSinceLastReport / ((currentTime - lastFpsTime) / 1000.0); System.out.printf("[性能] FPS: %.1f%n", fps); framesSinceLastReport = 0; lastFpsTime = currentTime; } } catch (InterruptedException e) { System.out.println("[线程] 采集线程被中断"); Thread.currentThread().interrupt(); break; } catch (Exception e) { System.err.println("采集线程错误: " + e.getMessage()); e.printStackTrace(); } } System.out.println("[采集线程] 安全退出"); } /** * 优化的帧处理 */ private static void processFrame(Mat frameMat) { try { // 转换颜色空间 if (displayMat == null) { displayMat = new Mat(DEVICE_HEIGHT, DEVICE_WIDTH, frameMat.type()); } Imgproc.cvtColor(frameMat, displayMat, Imgproc.COLOR_BGR2RGB); // 获取帧参数 final int width = displayMat.cols(); final int height = displayMat.rows(); // 初始化或复用BufferedImage BufferedImage bufImg = bufImgRef.get(); if (bufImg == null || bufImg.getWidth() != width || bufImg.getHeight() != height) { bufImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); bufImgRef.set(bufImg); } // 直接像素复制 byte[] imgPixels = ((DataBufferByte) bufImg.getRaster().getDataBuffer()).getData(); displayMat.get(0, 0, imgPixels); // JavaFX线程安全更新 Platform.runLater(() -> { if (!isRunning.get() || imageView == null) return; // 获取或创建JavaFX图像 WritableImage fxImg = fxImgRef.get(); if (fxImg == null || (int) fxImg.getWidth() != width || (int) fxImg.getHeight() != height) { fxImg = new WritableImage(width, height); fxImgRef.set(fxImg); } // 直接写入像素数据 PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance(); fxImg.getPixelWriter().setPixels(0, 0, width, height, pixelFormat, imgPixels, 0, width * 3); // 设置图像 imageView.setImage(fxImg); }); } catch (Exception e) { System.err.println("帧处理错误: " + e.getMessage()); } finally { // 释放资源 if (frameMat != null) { frameMat.release(); } // 补充新的Mat到池中 if (matPool.remainingCapacity() > 0) { matPool.offer(new Mat()); } } } /** * 检查性能并释放资源 */ private static void checkPerformance() { // 检查内存使用 long usedMemoryMB = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024); System.out.printf("[性能监控] 内存使用: %d MB%n", usedMemoryMB); if (usedMemoryMB > MAX_MEMORY_MB) { System.err.printf("[性能警告] 内存使用超过阈值 (%d > %d MB),尝试释放资源%n", usedMemoryMB, MAX_MEMORY_MB); releaseUnusedResources(); } // 检查CPU使用率 double cpuUsage = calculateCpuUsage(); System.out.printf("[性能监控] CPU使用率: %.1f%%%n", cpuUsage); if (cpuUsage > MAX_CPU_USAGE) { System.err.printf("[性能警告] CPU使用率超过阈值 (%.1f > %.1f%%),尝试优化%n", cpuUsage, MAX_CPU_USAGE); optimizeForCpu(); } } /** * 计算CPU使用率 */ private static double calculateCpuUsage() { long now = System.nanoTime(); long currentTime = System.currentTimeMillis(); if (currentTime - lastCpuCheckTime < CPU_CHECK_INTERVAL) { return 0.0; } long elapsedTime = currentTime - lastCpuCheckTime; lastCpuCheckTime = currentTime; long elapsedCpu = now - lastCpuTime; lastCpuTime = now; // 简单估算CPU使用率 double cpuUsage = (elapsedCpu / 1e6) / elapsedTime * 100.0; // 限制在合理范围内 return Math.min(100.0, Math.max(0.0, cpuUsage)); } /** * 释放未使用的资源 */ private static void releaseUnusedResources() { System.out.println("[资源优化] 释放未使用资源"); // 释放Mat池中多余资源 synchronized (matPool) { int excess = matPool.size() - MAT_POOL_SIZE; if (excess > 0) { for (int i = 0; i < excess; i++) { Mat mat = matPool.poll(); if (mat != null) { mat.release(); } } System.out.printf("[资源优化] 释放了 %d 个Mat对象%n", excess); } } // 释放BufferedImage和WritableImage if (bufImgRef.get() != null) { bufImgRef.set(null); System.out.println("[资源优化] 释放BufferedImage"); } if (fxImgRef.get() != null) { fxImgRef.set(null); System.out.println("[资源优化] 释放WritableImage"); } // 强制垃圾回收 System.gc(); } /** * CPU优化策略 */ private static void optimizeForCpu() { System.out.println("[CPU优化] 降低处理负载"); // 降低帧率目标 // 这里可以根据实际情况调整帧率 // 但需要确保不影响基本功能 // 跳过一些帧处理 frameCounter.incrementAndGet(); if (frameCounter.get() > 1) { frameCounter.set(0); System.out.println("[CPU优化] 跳过一帧处理"); } } /** * 全局资源清理 */ private static synchronized void cleanupResources() { System.out.println("[资源清理] 开始清理资源"); isRunning.set(false); System.out.println("[状态] isRunning 设置为 false"); // 关闭视频捕获 if (videoCapture != null) { if (videoCapture.isOpened()) { videoCapture.release(); System.out.println("[资源] VideoCapture已释放"); } videoCapture = null; } // 释放Mat对象池 synchronized (matPool) { Mat mat; while ((mat = matPool.poll()) != null) { if (!mat.empty()) { mat.release(); } } System.out.println("[资源] Mat对象池已清空"); } // 释放显示Mat if (displayMat != null) { displayMat.release(); displayMat = null; System.out.println("[资源] displayMat已释放"); } // 关闭线程池 if (frameProcessingPool != null) { try { frameProcessingPool.shutdown(); if (!frameProcessingPool.awaitTermination(500, TimeUnit.MILLISECONDS)) { frameProcessingPool.shutdownNow(); } System.out.println("[线程池] 帧处理线程池已关闭"); } catch (InterruptedException e) { frameProcessingPool.shutdownNow(); Thread.currentThread().interrupt(); } } // 清空图像引用 Platform.runLater(() -> { if (imageView != null) { imageView.setImage(null); } }); bufImgRef.set(null); fxImgRef.set(null); System.out.println("[资源清理] 资源清理完成"); } /** * 停止采集 */ public static synchronized void stopCapture() { if (!isRunning.get()) { System.out.println("[停止采集] 采集未运行,无需停止"); return; } System.out.println("[停止采集] 请求停止..."); isRunning.set(false); if (captureThread != null && captureThread.isAlive()) { captureThread.interrupt(); try { captureThread.join(1000); System.out.println("[线程] 采集线程已停止"); } catch (InterruptedException e) { System.out.println("[线程] 停止采集线程时被中断"); Thread.currentThread().interrupt(); } } cleanupResources(); System.out.println("[停止采集] 资源已释放"); } } 目前cpu的使用率还是很高,给出优化后的完整代码
10-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值