我们有一个广泛部署的应用程序(运行它的几百个工作站).在一个站点(并且只有一个站点 - 我们的产品被广泛部署到许多环境中),我们随机出现以下错误:
java.lang.OutOfMemoryError:无法在java.lang.Thread.start(未知来源)的java.lang.Thread.start0(本机方法)中创建新的本机线程
操作系统是Windows 7 64位我们运行在32位JVM(1.7.0_45)
使用Windows任务管理器,我可以看到该进程有39个本机线程(不是很多),所以我们的应用程序中没有线程泄漏...没有其他进程占用大量线程(Explorer有35个, jvisualvm有24个,IEXPLORE有20个,...我没有确切的数目,但我们可能看也许 300线程的用户总数).
我试图附加JVisualVM,但它无法连接到进程(可能是线程耗尽的b/c).但是根据我可以从JVisualVM获得的指标,Java线程的数量约为22个实时和11个守护进程.
堆很好 - 堆是500MB,实际使用250MB.
该过程使用-Xmx512m启动
我们的流程显示内存使用情况(在任务管理器中)为597,744K.
工作站有8GB RAM,其中只使用3.8-4.0GB(我知道,32位进程无法访问所有这些,但仍然有很多)
使用的VMMap,堆栈大小为49,920KB,承诺为2,284K.
该过程显示5358KB空闲,空闲列表中最大的可分配块大小为1,024K.
我使用了资源监视器,它显示提交(KB)为630428,工作集(KB)为676,996,可共享(KB)为79,252,私有(KB)为597,744
我完全不知道这里发生了什么.我已经阅读了大量关于此的文章,这听起来像在一些Linux系统上,每个用户的线程限制可能会导致问题(但这不是Linux,其他文章中描述的问题通常谈论需要成千上万的线程 - 绝对不是我们的情况.
如果我们的堆非常大,我可以看到进入空间可用于线程,但500MB似乎是一个非常合理的小堆(特别是对于具有8GB RAM的工作站).
所以我已经把我所知道的所有事情都耗尽了 - 有没有人对这里可能发生的事情有任何额外的指示?
编辑1:
我发现这篇有趣的文章:Eclipse崩溃了"无法创建新的本机线程" - 任何想法?(我的设置和信息)
他们建议堆栈大小可能是问题.
本文:在哪里可以找到Sun/Oracle JVM的默认XSS值? - 提供Oracle文档的链接,指出默认堆栈大小为512KB.因此,如果我的应用程序有大约40个线程,我们正在寻找20 MB的堆栈.500MB堆.这一切似乎都在32位Java进程的正常范围内.
这让我有两个可以想到的可能性:
那么,有没有关于如何检查内存分段的指针?
编辑2:
链接到@mikhael的文章(http://blog.egilh.com/2006/06/2811aspx.html)对32位JVM上允许的#个线程进行了粗略计算.
我要假设:
操作系统进程空间限制:2GB现代JVM需要250MB(这是一个很大的假设 - 我只是将链接文章中的内容翻了一倍)堆栈大小(默认Oracle):512KB堆:512MB PermGen :(不记得确切,但它是肯定少于100MB,所以让我们使用它)
所以我有一个最糟糕的情况:(2GB - .25GB - .5GB - .1GB)/.005GB = 230线程
编辑3:
我最初应该包含的信息:在此问题发生之前,应用程序运行良好(例如24到48小时).应用程序进行连续后台处理,因此空闲时间非常短.不确定这是否重要......
编辑4:
更多信息:从另一个故障看VMMap,我看到本机堆耗尽.
堆大小为1.2GB,仅承诺59.8MB.
Java运行时是这里的问题,还是一些本机资源未正确发布的问题?就像一个未被发布的内存映射文件?
我们确实使用了内存映射文件,因此我将重点放在那些文件上.
编辑4:
我认为我已将问题跟踪到发生如下异常:
java.lang.OutOfMemoryError
at …Run Code Online (Sandbox Code Playgroud) 我们需要根据"类型"字段跟踪帐户集合中记录的记录计数.因此,我们想知道TYPE1中有多少个帐户,TYPE2中有多少个帐户等...此外,我们需要知道每个帐户中"金额"字段的总数.
聚合查询对我们来说不够快(这些计数需要在UI中实时更新,我们将拥有数百万条记录,需要花费很多秒才能运行的聚合查询不会削减它),所以我正在寻找一个单独的总计集合,其中一个对象可以跟踪每种类型的计数器.
当我们更改'type'字段的值(即将帐户从一种类型移动到另一种类型)时,我们需要调整计数和'value'总计(减少原始类型的计数器,增加新类型的计数器) ).然后,我们可以使用带有$ incr()的更新命令来调整存储类型计数和值总和的总计记录中的字段.(这确实意味着我们为每个'类型'更新都有两个数据库写入,但我没有看到解决方法,除非有人有建议).
对于单个记录调整,这非常简单 - 我们可以捕获数据访问层中的类型更改,并在总计跟踪对象中进行二次更新.
问题是如何跟踪'金额'总计.对于单个记录调整,这不是问题.但对于批量操作(db.collection.update()可能影响数千条记录),我们需要获取每个调整记录的"金额"字段的总和.
到目前为止,我还没有能够轻松找到让Mongo获取我需要的信息的方法.
我有一个策略,包括在Account对象中添加一个标记的历史数组,其中包含唯一的"changeId"和文档记录在更改时的"金额",然后针对该历史记录运行聚合changeId得到总数.然后可选择删除历史记录(或在定期清理过程中执行此操作).
例如,如果我进行了批量更改,我会生成一个唯一的ID(以下为'aaaaaaa'),然后在调整'type'的批量更新中为历史记录执行数组插入:
{
"amount": 123,
"type": "TYPE1",
"history": [
{
"changeId": "aaaaaaaaaa",
"amount": 123,
"oldType": "TYPE2",
"newType": "TYPE1"
}
]
}
Run Code Online (Sandbox Code Playgroud)
然后我可以做一个聚合,它给我刚刚运行的changeId的'amount'的总和.
我认为这会奏效,但它很笨拙 - 有更好的方法吗?
适用于 Outlook 的 Dynamics 插件通过 Internet Explorer 嵌入窗口显示内容。我试图找到一种方法来获取该嵌入窗口的 SHDocVw.InternetExplorer COM 对象。我们的应用程序是独立运行的(它不是 Outlook 或 IE 加载项),我们根本无法控制嵌入式 IE 窗口的创建。
当我使用:
Dim SWs As SHDocVw.ShellWindows
Set SWs = New SHDocVw.ShellWindows
Run Code Online (Sandbox Code Playgroud)
SWs 集合不包含对 Outlook 中嵌入式浏览器的引用(尽管我确实得到了对常规浏览器窗口的引用)。
使用 Spy++,我看到嵌入式 Outlook 窗口的以下窗口层次结构:
Window "xxxxxx" WindowsForms10.Window.8.app.0.5c39d4_r64_ad2
- "" Shell Embedding
- "" Shell DocObject View
- "" Internet Explorer_Server
Run Code Online (Sandbox Code Playgroud)
层次结构中的最后两个窗口(Shell DocObject View 和 Internet Explorer_Server)与正在运行的 Internet Explorer 实例中的嵌入式查看器完全相同。
似乎必须有某种方法来获得对这些嵌入式浏览器的 COM 引用 - 任何帮助或想法将不胜感激。
我们需要能够更改给定打印机的默认选定打印托盘.有没有人有VC++/win32代码这样做?
如果重要,我相信我们必须更改打印机的默认设置.我们的打印作业由我们以外的应用程序执行,因此我们无法在源自应用程序内部的打印操作的上下文中进行这些更改.除非有某种方法可以修改不同应用程序中的默认打印设置,否则我认为我们无法更改用户的打印机默认设置,启动打印作业,然后将默认设置恢复为原始值.
我们真的更喜欢只为当前用户更改默认值,而不需要任何特殊的UAC提升等...
我怀疑它将使用类似于此MSDN文章中显示的内容,并涉及在DEVMODE结构中设置字段(dmDefaultSource或dmFormName或两者).
任何接受者?或者有没有人有任何他们想分享的陷阱?
编辑:这是DEVMODE文档DEVMODE文档的链接
编辑:我还应该指出,我们正在寻找一个通用的解决方案 - 不是特定于特定打印机的东西(我们在很多很多环境中部署)
我们的应用程序允许在文件选择对话框中选择多个文件,该对话框通过GetOpenFileName函数显示(此问题也适用于使用CFileDialog等的用户)
可以在文件名字段中键入的字符数似乎受到限制(259似乎是魔幻数字-不知道为什么)。
我们尝试更改OPENFILENAME结构的以下成员:
lpstrFile-指向我们自己的缓冲区,大小为4K字节nMaxFile-设置为lpstrFile的大小(我们正在编译ANSI,所以实际上是4000
但是这些值似乎不会增加对话框中文件名字段的输入宽度。
我将尝试向控件发送EM_SETLIMITTEXT消息,但想知道是否还有其他解决方案。
编辑-我自己解决了这个问题:解决方案 我无法接受自己的回答,但这是后代的问题。如果其他人有更好的解决方案,请发布它-或随意修改我的解决方案,以便将来的搜索者将其放在顶部。
我在嵌入式 Web 服务器(Jetty)中运行 RESTEasy。
我的资源需要访问支持数据存储,其配置是在用于启动我们的应用程序的命令行上传递的。
理想情况下,我将在资源的构造函数中注入支持数据存储资源:
@Path("/")
public class MappingService{
private final RecordManager recman;
public MappingService(RecordManager recman) {
this.recman = recman;
}
@PUT
@Path("/mapping/{key}")
@Produces({MediaType.APPLICATION_JSON, "application/*+json"})
public Response createMapping(@PathParam("key") String key, @QueryParam("val") String val) {
// do stuff with recman ...
}
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我会在应用程序的其他位置配置 RecordManager 对象,然后将其提供给 MappingService 构造函数。
我发现我可以使用 Application 对象返回特定的单例实例。但看起来 RESTEasy 自己构造了应用程序对象 - 所以我没有看到任何将配置对象传递到应用程序单例的方法。
那么:如何将外部实例化的对象传递到我的资源处理程序中?
我发现这篇文章(在 JAX-RS 请求之间共享变量)描述了如何使用上下文侦听器将对象添加到上下文并将其拉入资源处理程序中 - 但这太可怕了 - 我必须采用 POJO突然间我让它高度依赖于它的容器。请告诉我有更好的方法!
我有两台运行在不同分辨率的显示器.左侧监视器为1920x1200.右侧监视器(主监视器)为1920x1080.
我想使用SetWindowPos使窗口占据左手监视器的完整垂直高度.
这是我做的:
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
hMonitor = monitorFromPoint(x, 0, MONITOR_DEFAULTTONEAREST);
MONITORINFO moninfo;
moninfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, moninfo);
height = moninfo.rcWork.bottom - moninfo.rcWork.top;
SetWindowPos(hwnd, 0, moninfo.rcWork.left, moninfo.rcWord.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
Run Code Online (Sandbox Code Playgroud)
我已经确认高度计算到1200(预期b/c是目标监视器的垂直分辨率).
但是,在调用SetWindowPos之后,窗口矩形不会填满整个屏幕高度(实际上是1080高).
我甚至在VBA中试过这个只是为了咯咯笑:
Public Sub testSWP()
Dim hwnd As Long
hwnd = &H1D2F2C
SetWindowPos &H1D2F2C, 0, -1900, 0, 150, 1200, SWP_NOZORDER Or SWP_NOACTIVATE
Dim r As RECT
GetWindowRect hwnd, r
' at this point, r.bottom = 1080
End Sub
Run Code Online (Sandbox Code Playgroud)
这很好(GetWindowRect文档说坐标将在客户端空间,我假设win32正在我的主显示器和辅助显示器的分辨率之间进行转换.
我正准备通过目标和主监视器的高度比率来扩大垂直维度.我敢肯定这是去工作,但似乎像很多篮球的有跳过 - 我我也许只是没有意识到确定"客户"的坐标屏幕尺寸的更好的办法?
想知道是否有人有从Java进行DDE调用的经验和/或示例代码.我已经使用stddde库中的win32调用(DdeInitialize,DdeClientTransaction)完成了DDE,并且可以为此编写一个JNI包装器,但我认为从JNA执行它可能会很好
我也有一些担心DDE调用需要从带有消息泵的线程发生的事实,我并不完全确定如何在Java中强制它.
我们将要做的调用非常简单(相当于VBA的DDInitiate,DDEExcecute和DDETerminate函数).
我们正在使用提供json的REST服务,该服务将包含一些标准属性以及许多动态属性.
例如:
{
id: 123,
name: "some name",
custom_name: "Some value",
some_other_custom_name: "Some other value",
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望课程设计如下:
public class MyObject{
@JsonProperty int id;
@JsonProperty String name;
private Map<String, String> customVals;
public int getId(){
return id;
}
public String getName(){
return name;
}
public String getCustomVal(String key){
return customVals.get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
有没有办法说服杰克逊将自定义值推入Map(或实现等效功能)?
现在,我只是将整个对象反序列化为一个Map,并将其包装在我的业务对象中,但它并不像反序列化处理它那样优雅.
我们使用jdbc-odbc桥连接到MS SQL数据库.执行插入或更新时,将字符串放入填充到数据库字段长度的数据库中.有没有办法关闭这种行为(字符串应该没有填充进入表)?
作为参考,我们能够使用SQL管理工具和查询分析器插入不包含填充的字段值,所以我很确定这是在jdbc或odbc层事件中发生的.
编辑:数据库中的字段列为nvarchar(X),其中X = 50,255等等
编辑2:执行插入的调用是使用预准备语句,如:
PreparedStatement stmt = new con.prepareStatement("INSERT INTO....");
stmt.setString(1, "somevalue");
Run Code Online (Sandbox Code Playgroud) 我们在其中一个应用程序的生产服务器上看到原子池资源耗尽.
使用奇妙的AtomTableMonitor工具,我们已经通过RegisterWindowsMessage调用将问题分离为创建大量原子.他们都有这样的名字:
ControlOfs030D000000000270
最后的数字在哪里变化.
我的问题是:我们如何确定哪个过程正在创建这些原子?
一些潜在的资源:
https://blogs.msdn.microsoft.com/ntdebugging/2012/01/31/identifying-global-atom-table-leaks/
我正在尝试构建一个如下所示的过滤器表达式:
{ $expr:{ $gt:['$bal1', '$bal2'] } }
Run Code Online (Sandbox Code Playgroud)
使用 Filter.expr 函数:
Bson filter = Filters.expr( gt("$bal1", "$bal2") );
BsonDocument doc = filter.toBsonDocument(BsonDocument.class, collection.getCodecRegistry());
System.out.println(doc.toJson());
Run Code Online (Sandbox Code Playgroud)
这会产生以下 json:
{ "$expr" : { "$bal1" : { "$gt" : "$bal2" } } }
Run Code Online (Sandbox Code Playgroud)
显然这是不对的。有什么方法可以使用 Java 静态导入接口创建此查询,还是我不得不手动构造字符串?我是 Mongo 的新手,我无法想象每个人都在手工构建字符串 - 任何指导将不胜感激。
MongoDB Java 驱动程序 3.6.1
想法是创建一个静态的方法,它将计算部分填充数组的平均工资.假设numEmployees保存数组中具有有效数据的元素数.numEmployees传递给该方法.
public static double getAverage(double[ ] numEmployees)
{
double total = 0;
double average;
for (int i = 0; i < numEmployees.length; i++)
total += numEmployees[i];
average = total / numEmployees.length;
return average;
}
Run Code Online (Sandbox Code Playgroud)
我是否需要在方法中添加一个部分来计算填充的数组?喜欢:
int count=0;
int p=0;
if (numEmployees[p]>0)
{
count++;
p++;
}
Run Code Online (Sandbox Code Playgroud)
或者我应该在消息中的for循环中添加一个部分并将我的总数更改为:
for (int i = 0; i < numEmployees.length || numEmployees>0; i++)
total += numEmployees[i];
Run Code Online (Sandbox Code Playgroud)
比进一步下来
average = total / i;
Run Code Online (Sandbox Code Playgroud)