使用 StringBuilder 的 Java CLI 应用程序性能

发布于 2024-09-12 21:09:58 字数 1498 浏览 12 评论 0原文

一般:
我正在编写一个套接字客户端,它始终从某个服务器端(远程服务器端)接收“市场”数据/报价(永无止境的循环)。
我将数据分成块,以便我可以使用它。
每个块大约包含200个字符,需要转换为数组。
块被划分后,它被解析成一个列表(这里没有问题)。

问题:
运行10分钟后CPU使用率达到40%。
我已成功隔离问题。
每个块都需要转换为json。
所以我现在给你解决问题的实际代码。
此代码每 300-400 MS 执行一次。
跳过此代码将使整个系统的 CPU 使用率保持在 1%-2%。

注意:
我已阅读此线程,但我没有看到任何解决方案。
在循环中重用 StringBuilder 是否更好?

代码:

private static StringBuffer jsonVal = new StringBuffer();

    public static String toJson(List<QuotesData> quotesData) {
        // Empty variable
        jsonVal.delete(0, jsonVal.length());
        jsonVal.append("{");
        synchronized (quotesData) {
            for (QuotesData quote : quotesData) {

                jsonVal.append("\"").append(quote.getSymbol()).append("\":[{");
                jsonVal.append("\"ask\":\"").append(quote.getAsk()).append(
                        "\",");
                jsonVal.append("\"bid\":\"").append(quote.getBid()).append(
                        "\",");
                jsonVal.append("\"time\":\"").append(quote.getDateTime())
                        .append("\"}],");

            }
            jsonVal.append("}");
            String returnString = jsonVal.toString();
            return returnString.toString().replace("}],}", "}]}");
        }
    }

General:
I am writing a socket client that receives "Market" data/quotes all the time (never ending loop) from some server side (distant one).
i am dividing the data in to chunks so i can use it.
each chunk contains about 200 characters and needs to be converted in to an array.
After a chunk was divided it is been parsed in to a List (No Problems here).

The problem:
The CPU usage is reaching to 40% after 10 minutes of running.
I have managed to isolate the problem.
Every chunk needs to be converted in to json.
So i am giving you now the actual code that does the problems.
this code executes every 300-400 MS.
skipping this code will leave the entire system with 1%-2% CPU usage.

Note:
I have read this thread but i don't see any solution there.
Is it better to reuse a StringBuilder in a loop?

The code:

private static StringBuffer jsonVal = new StringBuffer();

    public static String toJson(List<QuotesData> quotesData) {
        // Empty variable
        jsonVal.delete(0, jsonVal.length());
        jsonVal.append("{");
        synchronized (quotesData) {
            for (QuotesData quote : quotesData) {

                jsonVal.append("\"").append(quote.getSymbol()).append("\":[{");
                jsonVal.append("\"ask\":\"").append(quote.getAsk()).append(
                        "\",");
                jsonVal.append("\"bid\":\"").append(quote.getBid()).append(
                        "\",");
                jsonVal.append("\"time\":\"").append(quote.getDateTime())
                        .append("\"}],");

            }
            jsonVal.append("}");
            String returnString = jsonVal.toString();
            return returnString.toString().replace("}],}", "}]}");
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

倒带 2024-09-19 21:09:58

首先,我建议使用 JProfiler 或 JConsole(两者都包含在 JDK6 中)来精确定位性能受到影响的位置。

在不知道 CPU 使用情况的情况下,我会避免同步。我怀疑 append 是问题所在。通过删除 static 本地 jsonVal 来清理它。

public static String toJson(final List<QuotesData> quotesData) {
    final List<QuotesData> theData = new ArrayList<QuotesData>(quotesData);
    StringBuffer jsonVal = new StringBuffer();
    jsonVal.append("{");
    for (QuotesData quote : quotesData) {
        jsonVal.append("\"").append(quote.getSymbol()).append("\":[{");
        jsonVal.append("\"ask\":\"").append(quote.getAsk()).append(
                "\",");
        jsonVal.append("\"bid\":\"").append(quote.getBid()).append(
                "\",");
        jsonVal.append("\"time\":\"").append(quote.getDateTime())
               .append("\"}],");

    }
    jsonVal.append("}");
    String returnString = jsonVal.toString();
    return returnString.toString().replace("}],}", "}]}");
}

考虑使用 Gson 等 JSON 库。代码变得更加简单。如果需要,您可以调整输出:

private static final Gson gson = new Gson();
public static String toJson(final List<QuotesData> quotesData) {
    return gson.toJson(new ArrayList<QuoteData>(quotesData));
}

First I would suggest using JProfiler or JConsole, both included in JDK6, to pinpoint exactly where the performance hit is.

Without knowing where the CPU usage is, I would avoid synchronized. I doubt append is the problem. Clean it up by getting rid of the static local jsonVal, too.

public static String toJson(final List<QuotesData> quotesData) {
    final List<QuotesData> theData = new ArrayList<QuotesData>(quotesData);
    StringBuffer jsonVal = new StringBuffer();
    jsonVal.append("{");
    for (QuotesData quote : quotesData) {
        jsonVal.append("\"").append(quote.getSymbol()).append("\":[{");
        jsonVal.append("\"ask\":\"").append(quote.getAsk()).append(
                "\",");
        jsonVal.append("\"bid\":\"").append(quote.getBid()).append(
                "\",");
        jsonVal.append("\"time\":\"").append(quote.getDateTime())
               .append("\"}],");

    }
    jsonVal.append("}");
    String returnString = jsonVal.toString();
    return returnString.toString().replace("}],}", "}]}");
}

Consider using a JSON library like Gson. The code becomes much simpler. You can tweak the output if needed:

private static final Gson gson = new Gson();
public static String toJson(final List<QuotesData> quotesData) {
    return gson.toJson(new ArrayList<QuoteData>(quotesData));
}
权谋诡计 2024-09-19 21:09:58

我的客人是 StringBuilder 不断调整大小。有多少个报价数据?我建议您在 for 循环之前创建一个具有大小的 StringBuilder:

StringBuffer jsonVal = new StringBuffer(quotesData.size()*200); //the 200 is on top of my head. Do a few loop to see what is the average length of a QuotesData.

顺便说一句,您是否考虑过使用 StringBuilder 来代替?它与 StringBuffer 相同,减去线程安全的开销(StringBuffer 是同步的,StringBuild 不是)。

My guest is that StringBuilder is constantly being resized. How many quotesData there is? I suggest you create a StringBuilder with a size before the for loop:

StringBuffer jsonVal = new StringBuffer(quotesData.size()*200); //the 200 is on top of my head. Do a few loop to see what is the average length of a QuotesData.

By the way, have you considered using StringBuilder instead? It's the same as StringBuffer, minus the overhead of being thread-safe (StringBuffer is synchronized, StringBuild is not).

寄居者 2024-09-19 21:09:58

好的,这看起来像是过度优化的典型案例。
对象创建并不那么昂贵,您需要重写相同的字符串缓冲区,特别是如果每​​ 300-400 毫秒调用一次。

我将尝试解决所有可能的情况:
指数增长
上面的代码每 300 毫秒分配给一个新线程,但列表非常庞大,需要超过 300 毫秒才能序列化。如果是这种情况,您基本上会阻塞您的资源,应用程序崩溃只是时间问题。
如果是这种情况,您应该会看到 CPU 不断上升。
解决方案是:

  1. 限制可以并发运行的线程数量,这样就不会终止应用程序
  2. 并发构建 json 对象并合并结果,这样构建单个 json 所需的时间少于 300 毫秒。

加速
好的,所以列表是不可克隆的,我假设这意味着它不是真正的列表,而是作为列表接口实现的某种队列。因此,保持同步不变,我会这样做:

public static final int JSON_LENGTH = 250; //you probably know this

public static String toJson(final List<QuotesData> quotesData) {
    jsonVal = new StringBuilder(JSON_LENGTH * quotesData.size());
    jsonVal.append("{");
    synchronized (quotesData) {
        for (QuotesData quote : quotesData) {

            jsonVal.append("\"").append(quote.getSymbol()).append("\":[{")
            .append("\"ask\":\"").append(quote.getAsk()).append("\",")
            .append("\"bid\":\"").append(quote.getBid()).append("\",")
            .append("\"time\":\"").append(quote.getDateTime()).append("\"}],");

        }
        // much much faster than replace
        jsonVal.setCharAt(jsonVal.length()-1, '}');
        return jsonVal.toString();
    }
}

大多数更改都是装饰性的,并且我非常确定 JIT 已经对它们进行了优化。我要做的唯一区别是使用 StringBuilder 并每次创建一个新的,而不是使用 .replace()
但为了进一步强调我的观点,除非你符合第一个描述(指数增长),否则我怀疑问题就在这里。我会先看看你列出的实施情况。

OK, so this looks like a classic case of over optimization.
Object creation isn't that expensive that you need to rewrite the same string buffer, especially if this is called every 300-400ms.

I'll try to address every possible scenario:
Exponential growth
The above code is assigned to a new thread every 300ms but the list is enormous and takes over 300ms to serialize. If this is the case you are basically choking your resources and it is only a matter of time before the application crashes.
If this is the case, you should see the CPU constantly rising.
The solution would be to:

  1. Limit the number of threads that can run concurrently so you don't kill the application
  2. Concurrently build the json object and merge the result so building a single json takes less then 300ms.

Speedups
OK, so the list isn't clonable which I'm assuming means it isn't really a list but rather some sort of queue implemented as a list interface. So leaving synchronization as is I would do this:

public static final int JSON_LENGTH = 250; //you probably know this

public static String toJson(final List<QuotesData> quotesData) {
    jsonVal = new StringBuilder(JSON_LENGTH * quotesData.size());
    jsonVal.append("{");
    synchronized (quotesData) {
        for (QuotesData quote : quotesData) {

            jsonVal.append("\"").append(quote.getSymbol()).append("\":[{")
            .append("\"ask\":\"").append(quote.getAsk()).append("\",")
            .append("\"bid\":\"").append(quote.getBid()).append("\",")
            .append("\"time\":\"").append(quote.getDateTime()).append("\"}],");

        }
        // much much faster than replace
        jsonVal.setCharAt(jsonVal.length()-1, '}');
        return jsonVal.toString();
    }
}

Most of the changes are cosmetic, and I'm pretty sure that the JIT would already optimize them. Only difference I would do is use StringBuilder and create a new one each time and don't use .replace().
But to stress my point further unless you fit the first description (exponential growth) I doubt the problem is here. I would look at you list implementation first.

贪了杯 2024-09-19 21:09:58

一些建议:

  • 分析代码,它应该向您显示热点。
  • 使用StringBuilder而不是StringBufferStringBuffer 是同步的,StringBuilder 不是。
  • synchronized 语句真的需要吗?如果没有,请尝试将其删除。
  • return 语句中不需要 toString()。您可以将其删除。
  • 修改代码,以便最终不需要 replace() 方法,如果 returnString 很长,那么成本可能会很高。
  • 尝试在循环之前创建一个新的 StringBuffer 对象,而不是清除旧的对象。
  • 尝试返回 interned 字符串的值,即返回 returnString.intern()

A few suggestions:

  • Profile the code, it should show you the hot spots.
  • Use StringBuilder instead of StringBuffer. StringBuffer is synchronized, StringBuilder is not.
  • Is the synchronized statement really needed? If not, try removing it.
  • toString() is not needed on the return statement. You can remove it.
  • Modify the code so that you don't need the replace() method in the end, that could be costly if returnString is long.
  • Try to create a new StringBuffer object before the loop instead clearing of the old one.
  • Try to return interned value of the string i.e. return returnString.intern()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文