You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在RCP 4应用中获取当前进程的Windows句柄信息

解决RCP 4应用中SWT句柄耗尽的监控方案

我完全理解你的痛点——生产环境碰到SWT因句柄耗尽抛出异常,但开发环境根本复现不了,还得用纯Java方案统计Windows的GDI/User Handles以及SWT内部资源的使用情况,完全绕开命令行。下面分模块给你具体实现思路和代码:

一、获取Windows系统级GDI/User Handles总数

Java本身没法直接调用Windows底层API,这里推荐用JNA(Java Native Access),不用写JNI代码,直接在Java里调用系统函数,特别适合RCP这类桌面应用。

1. 引入JNA依赖

因为是RCP应用,你可以把JNA的OSGi bundle加入target平台,或者在MANIFEST.MF里添加依赖:

Require-Bundle: com.sun.jna;bundle-version="5.13.0",
 com.sun.jna.platform;bundle-version="5.13.0"

如果用Maven构建,pom.xml里加:

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.13.0</version>
</dependency>
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform</artifactId>
    <version>5.13.0</version>
</dependency>

2. 系统句柄统计代码

通过JNA调用Windows的GetGuiResources函数,获取当前进程的GDI和User Handles数量:

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;

public class SystemHandleMonitor {
    public static long getGdiHandleCount() {
        WinNT.HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
        // 参数0代表获取GDI Handles数量
        return Kernel32.INSTANCE.GetGuiResources(processHandle, 0);
    }

    public static long getUserHandleCount() {
        WinNT.HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
        // 参数1代表获取User Handles数量
        return Kernel32.INSTANCE.GetGuiResources(processHandle, 1);
    }
}

二、追踪SWT内部资源使用情况

SWT会为每个Device(比如Display)维护资源计数,我们可以通过内部API获取图像、字体、复合控件等资源的具体数量,这对定位泄漏点至关重要。

SWT资源统计代码

注:这里用到的getDeviceData()是SWT内部API,在RCP 4常用的SWT 4.x版本中稳定,但如果后续SWT版本迭代,可能需要微调:

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.internal.win32.DeviceData;

public class SwtResourceMonitor {
    public static void logSwtResourceUsage(Display display) {
        DeviceData deviceData = display.getDeviceData();
        
        // 输出各类SWT资源的实时计数
        System.out.println("SWT已创建图像数量: " + deviceData.images.length);
        System.out.println("SWT已创建字体数量: " + deviceData.fonts.length);
        System.out.println("SWT已创建复合控件数量: " + deviceData.composites.length);
        System.out.println("SWT已创建控件总数: " + deviceData.controls.length);
        System.out.println("SWT已创建光标数量: " + deviceData.cursors.length);
    }
}

如果担心内部API的兼容性,也可以遍历Display的所有资源来统计,效率稍低但完全满足监控需求。

三、整合到RCP 4应用中

结合你提供的plugin.xml,你的应用基于org.eclipse.e4.ui.workbench.swt.E4Application,可以通过E4的生命周期扩展来启动监控:

1. 创建生命周期启动处理器

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.swt.widgets.Display;

public class HandleMonitorStartup {
    @Execute
    public void startMonitoring() {
        Display display = Display.getDefault();
        
        // 启动后台定时任务,每隔5分钟记录一次句柄信息
        new Thread(() -> {
            while (!display.isDisposed()) {
                try {
                    Thread.sleep(5 * 60 * 1000);
                    
                    // 切换到SWT主线程执行资源统计,避免线程安全问题
                    display.syncExec(() -> {
                        // 记录系统级句柄
                        long gdiCount = SystemHandleMonitor.getGdiHandleCount();
                        long userCount = SystemHandleMonitor.getUserHandleCount();
                        System.out.printf("系统GDI句柄数: %d, 系统User句柄数: %d%n", gdiCount, userCount);
                        
                        // 记录SWT内部资源
                        SwtResourceMonitor.logSwtResourceUsage(display);
                    });
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }).start();
    }
}

2. 在plugin.xml中注册扩展

添加org.eclipse.e4.core.contexts.runner扩展,让应用启动时自动执行监控逻辑:

<plugin>
    <!-- 你原有的product扩展 -->
    <extension id="product" point="org.eclipse.core.runtime.products">
        <product application="org.eclipse.e4.ui.workbench.swt.E4Application" name="appid">
            <property name="modelResourceHandler" value="bundleclass://a.model.resource.handler.ModelResourceHandler">
            </property>
        </product>
    </extension>
    
    <!-- 新增生命周期监控扩展 -->
    <extension point="org.eclipse.e4.core.contexts.runner">
        <runner class="com.yourpackage.HandleMonitorStartup" />
    </extension>
</plugin>

四、额外优化建议

  • 异常触发时立即记录:除了定时监控,还可以监听SWT异常(比如错误码为OS.ERROR_NO_HANDLESSWTError),发生时立即dump所有句柄信息,精准捕捉问题瞬间的状态。
  • 日志持久化:别只打印到控制台,把这些信息写到滚动日志文件里(用SLF4J+Logback等框架),方便后续回溯分析。
  • 第三方组件排查:如果监控到句柄持续增长,重点排查你引入的第三方SWT控件,大概率是第三方组件存在资源未释放的情况。

内容的提问来源于stack exchange,提问作者Michael

火山引擎 最新活动