【问题标题】:String TLAB allocation by socketRead0通过 socketRead0 分配字符串 TLAB
【发布时间】:2020-01-05 16:25:45
【问题描述】:

环境:

  1. Linux 4.x
  2. 异步分析器 1.6 (https://github.com/jvm-profiling-tools/async-profiler)
  3. OpenJDK8

应用程序代码:

通过 SocketInputStream 进行域-套接字通信

行动:

使用异步分析器运行应用程序:-d 60 -e alloc -f /tmp/alloc.svg

问题:

来自 SocketInputStream#socketRead0 的意外字符串分配

(青色:TLAB 分配)

socketRead 和 socketRead0 的 JDK 代码

private int socketRead(FileDescriptor fd,
                       byte b[], int off, int len,
                       int timeout)
    throws IOException {
    return socketRead0(fd, b, off, len, timeout);
}

private native int socketRead0(FileDescriptor fd,
                               byte b[], int off, int len,
                               int timeout)

原生 Socket-Impl:

  • jdk/src/solaris/native/java/net/SocketInputStream.c

假设:

字符串可能是通过JNI在Java堆中分配的,因为在字符串分配旁边的StackTrace中有一个SocketTimeoutException

Java_java_net_SocketInputStream_socketRead0(JNIEnv *env, jobject this,
                                            jobject fdObj, jbyteArray data,
                                            jint off, jint len, jint timeout)
{
    [...]
    if (timeout) {
        nread = NET_ReadWithTimeout(env, fd, bufP, len, timeout);
        if ((*env)->ExceptionCheck(env)) {
            if (bufP != BUF) {
                free(bufP);
            }
            return nread;
        }
    } else {
        nread = NET_Read(fd, bufP, len);
    }
    [...]
}

static int NET_ReadWithTimeout(JNIEnv *env, int fd, char *bufP, int len, long timeout) {
    int result = 0;
    long prevtime = NET_GetCurrentTime(), newtime;
    while (timeout > 0) {
        result = NET_TimeoutWithCurrentTime(fd, timeout, prevtime);
        if (result <= 0) {
            if (result == 0) {
                JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out");
            } else if (result == -1) {
                if (errno == EBADF) {
                    JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
                } else if (errno == ENOMEM) {
                    JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
                } else {
                    JNU_ThrowByNameWithMessageAndLastError
                            (env, "java/net/SocketException", "select/poll failed");
                }
            }
            return -1;
        }
        result = NET_NonBlockingRead(fd, bufP, len);
        if (result == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
            newtime = NET_GetCurrentTime();
            timeout -= newtime - prevtime;
            if (timeout > 0) {
                prevtime = newtime;
            }
        } else {
            break;
        }
    }
    return result;
}

我搜索了 C 代码并没有找到任何 jString 分配,所以我有点想法。

有人知道字符串分配可能发生在哪里吗?

【问题讨论】:

    标签: java jvm allocation async-profiler


    【解决方案1】:

    你的假设是正确的。 profile中的SocketTimeoutException告诉方法分配了一个异常对象,而这个异常对象有一个String消息也需要分配。

    我刚刚对 async-profiler 进行了更改,添加了 --cstack 选项,用于在分配分析模式下记录 C 堆栈和 Java 堆栈。证明java.lang.String确实是从JNI的ThrowNew函数中分配的:

    【讨论】:

    • 我从 master 构建了 async-profiler,事实上,我确实看到了与您在示例中描述的 --cstack 相同的行为。感谢您的贡献。
    猜你喜欢
    • 2014-12-08
    • 1970-01-01
    • 2020-07-31
    • 2014-05-22
    • 2016-04-28
    • 2014-02-05
    • 1970-01-01
    • 2011-06-22
    • 2018-08-08
    相关资源
    最近更新 更多