diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index 7dc942aa..1a20e129 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -21,3 +21,23 @@ You can kill the process by: * on Windows OS: run `taskkill /F /PID ...` all instances * on other OS: run `kill -9 ...` all instances + +### The Language Server Crashes Due to an Out Of Memory Error + +If you are working with large XML files or referencing large schema files, +it's likely that increasing the memory available to the language server will resolve this issue. + +If you aren't working with large XML files, then it may be a memory leak. +Please [file a issue](https://github.com/redhat-developer/vscode-xml/issues) with a description of what you were doing if this is the case. + +#### How to increase the amount of memory available to the Language Server + +If you are using the Java Language Server: +1. Go to settings +2. Navigate to the setting `xml.server.vmargs` +3. Add `-Xmx 1G` to the setting string. This allows the the language server to use at most 1 gigabyte of memory. +4. If the problem persists, you can increase the `1G` to `2G` or higher + +If you are using the binary language server: +1. By default, the language server will use up to 80% of your RAM. + Increasing this amount likely won't help solve the problem. diff --git a/package-lock.json b/package-lock.json index 76169914..0f3e2106 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-xml", - "version": "0.17.0", + "version": "0.17.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -135,12 +135,28 @@ "@types/node": "*" } }, + "@types/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, "@types/node": { "version": "10.17.54", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.54.tgz", diff --git a/package.json b/package.json index 9b4cbb55..05903a1c 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ ], "devDependencies": { "@types/fs-extra": "^8.0.0", + "@types/glob": "^7.1.4", "@types/node": "^10.14.16", "@types/vscode": "^1.37.0", "@types/yauzl": "^2.9.1", diff --git a/src/client/clientErrorHandler.ts b/src/client/clientErrorHandler.ts index 344bc2ad..19954832 100644 --- a/src/client/clientErrorHandler.ts +++ b/src/client/clientErrorHandler.ts @@ -1,9 +1,14 @@ -import { window } from "vscode"; +import * as fs from "fs-extra"; +import * as path from "path"; +import { commands, window } from "vscode"; import { CloseAction, ErrorAction, ErrorHandler, Message } from "vscode-languageclient"; +import { ClientCommandConstants } from "../commands/commandConstants"; +import glob = require("glob"); /** * An error handler that restarts the language server, - * unless it has been restarted 5 times in the last 3 minutes + * unless it has been restarted 5 times in the last 3 minutes, + * or if it crashed due to an Out Of Memory Error * * Adapted from [vscode-java](https://github.com/redhat-developer/vscode-java) */ @@ -23,6 +28,11 @@ export class ClientErrorHandler implements ErrorHandler { closed(): CloseAction { this.restarts.push(Date.now()); + const heapProfileGlob = new glob.GlobSync(`${path.resolve(__dirname, '..')}/java_*.hprof`); + if (heapProfileGlob.found.length) { + showOOMMessage(); + return CloseAction.DoNotRestart; + } if (this.restarts.length < 5) { return CloseAction.Restart; } else { @@ -37,3 +47,32 @@ export class ClientErrorHandler implements ErrorHandler { } } + +/** + * Deletes all the heap dumps generated by Out Of Memory errors + * + * @returns when the heap dumps have been deleted + */ +export async function cleanUpHeapDumps(): Promise { + const heapProfileGlob = new glob.GlobSync(`${path.resolve(__dirname, '..')}/java_*.hprof`); + for (let heapProfile of heapProfileGlob.found) { + await fs.remove(heapProfile); + } +} + +/** + * Shows a message about the server crashing due to an out of memory issue + */ +async function showOOMMessage(): Promise { + const DOCS = 'More info...'; + const result = await window.showErrorMessage('The XML Language Server crashed due to an Out Of Memory Error, and will not be restarted. ', // + DOCS); + if (result === DOCS) { + await commands.executeCommand(ClientCommandConstants.OPEN_DOCS, + { + page: 'Troubleshooting', + section: 'the-language-server-crashes-due-to-an-out-of-memory-error' + } + ); + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 5975b135..0104b8d6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,15 +16,16 @@ import { ExtensionContext, extensions, languages } from "vscode"; import { Executable, LanguageClient } from 'vscode-languageclient/node'; import { XMLExtensionApi } from './api/xmlExtensionApi'; import { getXmlExtensionApiImplementation } from './api/xmlExtensionApiImplementation'; +import { cleanUpHeapDumps } from './client/clientErrorHandler'; import { getIndentationRules } from './client/indentation'; import { startLanguageClient } from './client/xmlClient'; +import { registerClientOnlyCommands } from './commands/registerCommands'; import { collectXmlJavaExtensions } from './plugin'; import * as requirements from './server/requirements'; import { prepareExecutable } from './server/serverStarter'; import { ExternalXmlSettings } from "./settings/externalXmlSettings"; import { getXMLConfiguration } from './settings/settings'; import { Telemetry } from './telemetry'; -import { registerClientOnlyCommands } from './commands/registerCommands'; let languageClient: LanguageClient; @@ -52,6 +53,7 @@ export async function activate(context: ExtensionContext): Promise { await commands.executeCommand(ClientCommandConstants.OPEN_DOCS, { page: "Proxy" }); -} \ No newline at end of file +} diff --git a/src/server/java/javaServerStarter.ts b/src/server/java/javaServerStarter.ts index e12c5141..c7e8eae3 100644 --- a/src/server/java/javaServerStarter.ts +++ b/src/server/java/javaServerStarter.ts @@ -1,16 +1,31 @@ import * as os from 'os'; import * as path from 'path'; -import { ExtensionContext, workspace } from 'vscode'; +import { ExtensionContext, window, workspace } from 'vscode'; import { Executable } from 'vscode-languageclient/node'; import { getProxySettings, getProxySettingsAsJVMArgs, jvmArgsContainsProxySettings, ProxySettings } from '../../settings/proxySettings'; import { getJavaagentFlag, getKey, getXMLConfiguration, IS_WORKSPACE_VMARGS_XML_ALLOWED, xmlServerVmargs } from '../../settings/settings'; import { RequirementsData } from '../requirements'; -const glob = require('glob'); +import glob = require('glob'); declare var v8debug; const DEBUG = (typeof v8debug === 'object') || startedInDebugMode(); +/** + * Argument that tells the program to crash when an OutOfMemoryError is raised + */ +const CRASH_ON_OOM = '-XX:+ExitOnOutOfMemoryError'; + +/** + * Argument that tells the program to generate a heap dump file when an OutOfMemoryError is raised + */ +const HEAP_DUMP = '-XX:+HeapDumpOnOutOfMemoryError'; + +/** + * Argument that tells the program where to generate the heap dump that is created when an OutOfMemoryError is raised and `HEAP_DUMP` has been passed + */ +const HEAP_DUMP_LOCATION = '-XX:HeapDumpPath='; + export async function prepareJavaExecutable( context: ExtensionContext, requirements: RequirementsData, @@ -63,6 +78,18 @@ function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string params.push(watchParentProcess + 'false'); } } + if (vmargs.indexOf(CRASH_ON_OOM) < 0) { + params.push(CRASH_ON_OOM); + } + if (vmargs.indexOf(HEAP_DUMP) < 0) { + params.push(HEAP_DUMP); + } + if (vmargs.indexOf(HEAP_DUMP_LOCATION) < 0) { + params.push(`${HEAP_DUMP_LOCATION}${path.resolve(__dirname, '..')}`); + } else { + window.showWarningMessage('Heap dump location has been modified; if you are experiencing Out Of Memory crashes, vscode-xml won\'t be able to detect them'); + } + // "OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify // were deprecated in JDK 13 and will likely be removed in a future release." // so only add -noverify for older versions