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<String>();
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 =
094 ProcessUtil.execute(
095 ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jmap", "-histo:live",
096 String.valueOf(processId));
097
098 ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
099
100 String stdOutString = new String(objectValuePair.getKey());
101
102 if (!stdOutString.contains("#instances")) {
103 throw new IllegalStateException(
104 "JMap cannot connect to process ID " + processId);
105 }
106
107 byte[] stdErrBytes = objectValuePair.getValue();
108
109 if (stdErrBytes.length != 0) {
110 throw new IllegalStateException(
111 "JMap returns with error: " + new String(stdErrBytes));
112 }
113 }
114
115 private static void _checkJPS(int processId) throws Exception {
116 Future<ObjectValuePair<byte[], byte[]>> future = ProcessUtil.execute(
117 ProcessUtil.COLLECTOR_OUTPUT_PROCESSOR, "jps");
118
119 ObjectValuePair<byte[], byte[]> objectValuePair = future.get();
120
121 String stdOutString = new String(objectValuePair.getKey());
122
123 if (!stdOutString.contains(String.valueOf(processId))) {
124 throw new IllegalStateException(
125 "JPS cannot detect expected process ID " + processId);
126 }
127
128 byte[] stdErrBytes = objectValuePair.getValue();
129
130 if (stdErrBytes.length != 0) {
131 throw new IllegalStateException(
132 "JPS returns with error: " + new String(stdErrBytes));
133 }
134 }
135
136 private static int _getProcessId() {
137 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
138
139 String name = runtimeMXBean.getName();
140
141 int index = name.indexOf(CharPool.AT);
142
143 if (index == -1) {
144 throw new RuntimeException("Unable to parse process name " + name);
145 }
146
147 int pid = GetterUtil.getInteger(name.substring(0, index));
148
149 if (pid == 0) {
150 throw new RuntimeException("Unable to parse process name " + name);
151 }
152
153 return pid;
154 }
155
156 private static final int _PROCESS_ID;
157
158 private static final boolean _SUPPORTED;
159
160 private static final Log _log = LogFactoryUtil.getLog(HeapUtil.class);
161
162 static {
163 int processId = -1;
164 boolean supported = false;
165
166 if (JavaDetector.isOracle()) {
167 try {
168 processId = _getProcessId();
169
170 _checkJPS(processId);
171 _checkJMap(processId);
172
173 supported = true;
174 }
175 catch (Exception e) {
176 if (_log.isWarnEnabled()) {
177 _log.warn(HeapUtil.class.getName() + " is disabled", e);
178 }
179 }
180 }
181 else if (_log.isDebugEnabled()) {
182 _log.debug(
183 HeapUtil.class.getName() + " is only supported on Oracle JVMs");
184 }
185
186 _PROCESS_ID = processId;
187 _SUPPORTED = supported;
188 }
189
190 }