我有一个解析来自端点的JSON响应的函数。我用大约20k不同的输入调用这个函数。像这样:
for (Input input: inputList) {
// We create a new getRequest with the query
HttpGet getRequest = new HttpGet(input.getUrl());
// We make the getRequest accept application/json data
getRequest.addHeader("Accept", "application/json");
JSONParser jsonParser = new JSONParser();
JSONArray parsedArray = new JSONArray();
CloseableHttpResponse response = httpClient.execute(getRequest);
InputStream inputStream = response.getEntity().getContent();
parsedArray = (JSONArray) jsonParser.parse(new InputStreamReader(inputStream, "utf-8"));
parsedArrays.add(parsedArray);
response.close();
}
我正在使用json-simple
:
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
此时我正在循环中声明JSONParser,并且我正在思考它是否会更好地在它之外声明它。
最好只声明解析器一次,然后像参数一样将它传递给函数,或者JVM已经优化了这种东西吗?
答案 0 :(得分:1)
重用像解析器这样的对象总是一个好主意。即使对象的内存在您离开该方法并使用其范围后立即有资格进行垃圾收集,也无法保证这是立即完成的(VM决定),甚至是(如果解析器在某个地方存储对自身的引用,例如在静态列表中)。因此,这可能是您看到堆使用量增加的原因。在任何情况下,你都不喜欢VM。
另一方面是速度。每次构建解析器时,都会为您准备一些语法。每个结构都是一样的。在我监督的产品中,我们大量使用JAXB,并且必要的JAXBContext对象被实例化为惰性并且还保持重用,而不是为每次使用重建,因为从提供的类树构造它们是庞大且昂贵的。语法。
通常可以重用解析器,但有时它带有状态或不是线程安全的,这两者都是重用可能有问题的指示器。解析器类的文档将告诉您更多信息。
有时解析器由两部分组成 - 例如,JDK正则表达式实现使用可重用的Pattern对象并构成&#34;解析器&#34;的语法,以及作为实际解析器的Matcher对象,绑定到特定输入并将携带与该输入相关联的状态。匹配器不可重用,必须为每个输入重新创建,但语法本身,模式,可以全面重用。
答案 1 :(得分:1)
这取决于你是否从不同的线程调用该方法。在这种情况下,如果JSONParser不是线程安全的,那么可能存在同步问题,并且在方法中实例化JSONParser可能会更好。
如果您只有一个线程,则可以将JSONParser作为方法参数传递。
但我认为堆增长是因为输入数据不仅仅是JSONParser对象。
当堆已满时,垃圾收集器大部分被激活(在HotSpot中),因此它可能需要更长的时间才能激活gc。
如果你需要节省分配新对象的时间(new
,invokespecial
和构造函数指令),那么你应该关心这一点,但在这种情况下你需要考虑每一个对象创作,为此还有其他语言:)。让JVM完成它的工作:)。