/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.util;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.process.OutputProcessor;
import com.liferay.portal.kernel.process.ProcessUtil;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
/**
* @author Shuyang Zhou
*/
public class HeapUtil {
public static int getProcessId() {
if (!_SUPPORTED) {
throw new IllegalStateException(
HeapUtil.class.getName() + " does not support the current JVM");
}
return _PROCESS_ID;
}
public static <O, E> Future<ObjectValuePair<O, E>> heapDump(
boolean live, boolean binary, String file,
OutputProcessor<O, E> outputProcessor) {
return heapDump(_PROCESS_ID, live, binary, file, outputProcessor);
}
public static <O, E> Future<ObjectValuePair<O, E>> heapDump(
int processId, boolean live, boolean binary, String file,
OutputProcessor<O, E> outputProcessor) {
if (!_SUPPORTED) {
throw new IllegalStateException(
HeapUtil.class.getName() + " does not support the current JVM");
}
StringBundler sb = new StringBundler(5);
sb.append("-dump:");
if (live) {
sb.append("live,");
}
if (binary) {
sb.append("format=b,");
}
sb.append("file=");
sb.append(file);
List<String> arguments = new ArrayList<>();
arguments.add("jmap");
arguments.add(sb.toString());
arguments.add(String.valueOf(processId));
try {
return ProcessUtil.execute(outputProcessor, arguments);
}
catch (Exception e) {
throw new RuntimeException("Unable to perform heap dump", e);
}
}
public static boolean isSupported() {
return _SUPPORTED;
}
private static void _checkJMap(int processId) throws Exception {
Future<ObjectValuePair<byte[], byte[]>> future = ProcessUtil.execute(
ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jmap", "-histo:live",
String.valueOf(processId));
ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
String stdOutString = new String(objectValuePair.getKey());
if (!stdOutString.contains("#instances")) {
throw new IllegalStateException(
"JMap cannot connect to process ID " + processId);
}
byte[] stdErrBytes = objectValuePair.getValue();
if (stdErrBytes.length != 0) {
throw new IllegalStateException(
"JMap returns with error: " + new String(stdErrBytes));
}
}
private static void _checkJPS(int processId) throws Exception {
Future<ObjectValuePair<byte[], byte[]>> future = ProcessUtil.execute(
ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jps");
ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
String stdOutString = new String(objectValuePair.getKey());
if (!stdOutString.contains(String.valueOf(processId))) {
throw new IllegalStateException(
"JPS cannot detect expected process ID " + processId);
}
byte[] stdErrBytes = objectValuePair.getValue();
if (stdErrBytes.length != 0) {
throw new IllegalStateException(
"JPS returns with error: " + new String(stdErrBytes));
}
}
private static int _getProcessId() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
String name = runtimeMXBean.getName();
int index = name.indexOf(CharPool.AT);
if (index == -1) {
throw new RuntimeException("Unable to parse process name " + name);
}
int pid = GetterUtil.getInteger(name.substring(0, index));
if (pid == 0) {
throw new RuntimeException("Unable to parse process name " + name);
}
return pid;
}
private static final int _PROCESS_ID;
private static final boolean _SUPPORTED;
private static final Log _log = LogFactoryUtil.getLog(HeapUtil.class);
static {
int processId = -1;
boolean supported = false;
if (JavaDetector.isOracle()) {
try {
processId = _getProcessId();
_checkJPS(processId);
_checkJMap(processId);
supported = true;
}
catch (Exception e) {
if (_log.isWarnEnabled()) {
_log.warn(HeapUtil.class.getName() + " is disabled", e);
}
}
}
else if (_log.isDebugEnabled()) {
_log.debug(
HeapUtil.class.getName() + " is only supported on Oracle JVMs");
}
_PROCESS_ID = processId;
_SUPPORTED = supported;
}
}