001
014
015 package com.liferay.portal.kernel.util;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.process.OutputProcessor;
020 import com.liferay.portal.kernel.process.ProcessUtil;
021
022 import java.lang.management.ManagementFactory;
023 import java.lang.management.RuntimeMXBean;
024
025 import java.util.ArrayList;
026 import java.util.List;
027 import java.util.concurrent.Future;
028
029
032 public class HeapUtil {
033
034 public static int getProcessId() {
035 if (!_SUPPORTED) {
036 throw new IllegalStateException(
037 HeapUtil.class.getName() + " does not support the current JVM");
038 }
039
040 return _PROCESS_ID;
041 }
042
043 public static <O, E> Future<ObjectValuePair<O, E>> heapDump(
044 boolean live, boolean binary, String file,
045 OutputProcessor<O, E> outputProcessor) {
046
047 return heapDump(_PROCESS_ID, live, binary, file, outputProcessor);
048 }
049
050 public static <O, E> Future<ObjectValuePair<O, E>> heapDump(
051 int processId, boolean live, boolean binary, String file,
052 OutputProcessor<O, E> outputProcessor) {
053
054 if (!_SUPPORTED) {
055 throw new IllegalStateException(
056 HeapUtil.class.getName() + " does not support the current JVM");
057 }
058
059 StringBundler sb = new StringBundler(5);
060
061 sb.append("-dump:");
062
063 if (live) {
064 sb.append("live,");
065 }
066
067 if (binary) {
068 sb.append("format=b,");
069 }
070
071 sb.append("file=");
072 sb.append(file);
073
074 List<String> arguments = new ArrayList<>();
075
076 arguments.add("jmap");
077 arguments.add(sb.toString());
078 arguments.add(String.valueOf(processId));
079
080 try {
081 return ProcessUtil.execute(outputProcessor, arguments);
082 }
083 catch (Exception e) {
084 throw new RuntimeException("Unable to perform heap dump", e);
085 }
086 }
087
088 public static boolean isSupported() {
089 return _SUPPORTED;
090 }
091
092 private static void _checkJMap(int processId) throws Exception {
093 Future<ObjectValuePair<byte[], byte[]>> future = ProcessUtil.execute(
094 ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jmap", "-histo:live",
095 String.valueOf(processId));
096
097 ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
098
099 String stdOutString = new String(objectValuePair.getKey());
100
101 if (!stdOutString.contains("#instances")) {
102 throw new IllegalStateException(
103 "JMap cannot connect to process ID " + processId);
104 }
105
106 byte[] stdErrBytes = objectValuePair.getValue();
107
108 if (stdErrBytes.length != 0) {
109 throw new IllegalStateException(
110 "JMap returns with error: " + new String(stdErrBytes));
111 }
112 }
113
114 private static void _checkJPS(int processId) throws Exception {
115 Future<ObjectValuePair<byte[], byte[]>> future = ProcessUtil.execute(
116 ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jps");
117
118 ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
119
120 String stdOutString = new String(objectValuePair.getKey());
121
122 if (!stdOutString.contains(String.valueOf(processId))) {
123 throw new IllegalStateException(
124 "JPS cannot detect expected process ID " + processId);
125 }
126
127 byte[] stdErrBytes = objectValuePair.getValue();
128
129 if (stdErrBytes.length != 0) {
130 throw new IllegalStateException(
131 "JPS returns with error: " + new String(stdErrBytes));
132 }
133 }
134
135 private static int _getProcessId() {
136 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
137
138 String name = runtimeMXBean.getName();
139
140 int index = name.indexOf(CharPool.AT);
141
142 if (index == -1) {
143 throw new RuntimeException("Unable to parse process name " + name);
144 }
145
146 int pid = GetterUtil.getInteger(name.substring(0, index));
147
148 if (pid == 0) {
149 throw new RuntimeException("Unable to parse process name " + name);
150 }
151
152 return pid;
153 }
154
155 private static final int _PROCESS_ID;
156
157 private static final boolean _SUPPORTED;
158
159 private static final Log _log = LogFactoryUtil.getLog(HeapUtil.class);
160
161 static {
162 int processId = -1;
163 boolean supported = false;
164
165 if (JavaDetector.isOracle()) {
166 try {
167 processId = _getProcessId();
168
169 _checkJPS(processId);
170 _checkJMap(processId);
171
172 supported = true;
173 }
174 catch (Exception e) {
175 if (_log.isWarnEnabled()) {
176 _log.warn(HeapUtil.class.getName() + " is disabled", e);
177 }
178 }
179 }
180 else if (_log.isDebugEnabled()) {
181 _log.debug(
182 HeapUtil.class.getName() + " is only supported on Oracle JVMs");
183 }
184
185 _PROCESS_ID = processId;
186 _SUPPORTED = supported;
187 }
188
189 }