Merge branch cassandra-2.2 into cassandra-3.0

This commit is contained in:
Benjamin Lerer 2017-12-12 10:30:29 +01:00
commit dd187d105b
9 changed files with 650 additions and 560 deletions

View File

@ -17,6 +17,7 @@
* Mishandling of cells for removed/dropped columns when reading legacy files (CASSANDRA-13939) * Mishandling of cells for removed/dropped columns when reading legacy files (CASSANDRA-13939)
* Deserialise sstable metadata in nodetool verify (CASSANDRA-13922) * Deserialise sstable metadata in nodetool verify (CASSANDRA-13922)
Merged from 2.2: Merged from 2.2:
* Rely on the JVM to handle OutOfMemoryErrors (CASSANDRA-13006)
* Grab refs during scrub/index redistribution/cleanup (CASSANDRA-13873) * Grab refs during scrub/index redistribution/cleanup (CASSANDRA-13873)

View File

@ -18,8 +18,13 @@ using the provided 'sstableupgrade' tool.
Upgrading Upgrading
--------- ---------
- Nothing specific to this release, but please see previous upgrading sections, - Cassandra is now relying on the JVM options to properly shutdown on OutOfMemoryError. By default it will
especially if you are upgrading from 2.2. rely on the OnOutOfMemoryError option as the ExitOnOutOfMemoryError and CrashOnOutOfMemoryError options
are not supported by the older 1.7 and 1.8 JVMs. A warning will be logged at startup if none of those JVM
options are used. See CASSANDRA-13006 for more details.
- Cassandra is not logging anymore by default an Heap histogram on OutOfMemoryError. To enable that behavior
set the 'cassandra.printHeapHistogramOnOutOfMemoryError' System property to 'true'. See CASSANDRA-13006
for more details.
Materialized Views Materialized Views
------------------- -------------------

View File

@ -28,6 +28,7 @@
# #
# CLASSPATH -- A Java classpath containing everything necessary to run. # CLASSPATH -- A Java classpath containing everything necessary to run.
# JVM_OPTS -- Additional arguments to the JVM for heap size, etc # JVM_OPTS -- Additional arguments to the JVM for heap size, etc
# JVM_ON_OUT_OF_MEMORY_ERROR_OPT -- The OnOutOfMemoryError JVM option if specified
# CASSANDRA_CONF -- Directory containing Cassandra configuration files. # CASSANDRA_CONF -- Directory containing Cassandra configuration files.
# #
# As a convenience, a fragment of shell is sourced in order to set one or # As a convenience, a fragment of shell is sourced in order to set one or
@ -199,12 +200,22 @@ launch_service()
# to close stdout/stderr, but it's up to us not to background. # to close stdout/stderr, but it's up to us not to background.
if [ "x$foreground" != "x" ]; then if [ "x$foreground" != "x" ]; then
cassandra_parms="$cassandra_parms -Dcassandra-foreground=yes" cassandra_parms="$cassandra_parms -Dcassandra-foreground=yes"
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class" if [ "x$JVM_ON_OUT_OF_MEMORY_ERROR_OPT" != "x" ]; then
exec $NUMACTL "$JAVA" $JVM_OPTS "$JVM_ON_OUT_OF_MEMORY_ERROR_OPT" $cassandra_parms -cp "$CLASSPATH" $props "$class"
else
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class"
fi
# Startup CassandraDaemon, background it, and write the pid. # Startup CassandraDaemon, background it, and write the pid.
else else
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class" <&- & if [ "x$JVM_ON_OUT_OF_MEMORY_ERROR_OPT" != "x" ]; then
[ ! -z "$pidpath" ] && printf "%d" $! > "$pidpath" exec $NUMACTL "$JAVA" $JVM_OPTS "$JVM_ON_OUT_OF_MEMORY_ERROR_OPT" $cassandra_parms -cp "$CLASSPATH" $props "$class" <&- &
true [ ! -z "$pidpath" ] && printf "%d" $! > "$pidpath"
true
else
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class" <&- &
[ ! -z "$pidpath" ] && printf "%d" $! > "$pidpath"
true
fi
fi fi
return $? return $?

View File

@ -1,391 +1,391 @@
# #
# Licensed to the Apache Software Foundation (ASF) under one or more # Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with # contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. # this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0 # The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with # (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at # the License. You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# NOTE: All param tuning can be done in the SetCassandraEnvironment Function below # NOTE: All param tuning can be done in the SetCassandraEnvironment Function below
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function SetCassandraHome() Function SetCassandraHome()
{ {
if (! $env:CASSANDRA_HOME) if (! $env:CASSANDRA_HOME)
{ {
$cwd = [System.IO.Directory]::GetCurrentDirectory() $cwd = [System.IO.Directory]::GetCurrentDirectory()
$cwd = Split-Path $cwd -parent $cwd = Split-Path $cwd -parent
$env:CASSANDRA_HOME = $cwd -replace "\\", "/" $env:CASSANDRA_HOME = $cwd -replace "\\", "/"
} }
} }
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function SetCassandraMain() Function SetCassandraMain()
{ {
if (! $env:CASSANDRA_MAIN) if (! $env:CASSANDRA_MAIN)
{ {
$env:CASSANDRA_MAIN="org.apache.cassandra.service.CassandraDaemon" $env:CASSANDRA_MAIN="org.apache.cassandra.service.CassandraDaemon"
} }
} }
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function BuildClassPath Function BuildClassPath
{ {
$cp = """$env:CASSANDRA_HOME\conf""" $cp = """$env:CASSANDRA_HOME\conf"""
foreach ($file in Get-ChildItem "$env:CASSANDRA_HOME\lib\*.jar") foreach ($file in Get-ChildItem "$env:CASSANDRA_HOME\lib\*.jar")
{ {
$file = $file -replace "\\", "/" $file = $file -replace "\\", "/"
$cp = $cp + ";" + """$file""" $cp = $cp + ";" + """$file"""
} }
# Add build/classes/main so it works in development # Add build/classes/main so it works in development
$cp = $cp + ";" + """$env:CASSANDRA_HOME\build\classes\main"";""$env:CASSANDRA_HOME\build\classes\thrift""" $cp = $cp + ";" + """$env:CASSANDRA_HOME\build\classes\main"";""$env:CASSANDRA_HOME\build\classes\thrift"""
$env:CLASSPATH=$cp $env:CLASSPATH=$cp
} }
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function CalculateHeapSizes Function CalculateHeapSizes
{ {
# Check if swapping is enabled on the host and warn if so - reference CASSANDRA-7316 # Check if swapping is enabled on the host and warn if so - reference CASSANDRA-7316
$osInfo = Get-WmiObject -class "Win32_computersystem" $osInfo = Get-WmiObject -class "Win32_computersystem"
$autoPage = $osInfo.AutomaticManagedPageFile $autoPage = $osInfo.AutomaticManagedPageFile
if ($autoPage) if ($autoPage)
{ {
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "" echo ""
echo " WARNING! Automatic page file configuration detected." echo " WARNING! Automatic page file configuration detected."
echo " It is recommended that you disable swap when running Cassandra" echo " It is recommended that you disable swap when running Cassandra"
echo " for performance and stability reasons." echo " for performance and stability reasons."
echo "" echo ""
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
} }
else else
{ {
$pageFileInfo = Get-WmiObject -class "Win32_PageFileSetting" -EnableAllPrivileges $pageFileInfo = Get-WmiObject -class "Win32_PageFileSetting" -EnableAllPrivileges
$pageFileCount = $PageFileInfo.Count $pageFileCount = $PageFileInfo.Count
if ($pageFileInfo) if ($pageFileInfo)
{ {
$files = @() $files = @()
$sizes = @() $sizes = @()
$hasSizes = $FALSE $hasSizes = $FALSE
# PageFileCount isn't populated and obj comes back as single if there's only 1 # PageFileCount isn't populated and obj comes back as single if there's only 1
if ([string]::IsNullOrEmpty($PageFileCount)) if ([string]::IsNullOrEmpty($PageFileCount))
{ {
$PageFileCount = 1 $PageFileCount = 1
$files += $PageFileInfo.Name $files += $PageFileInfo.Name
if ($PageFileInfo.MaximumSize -ne 0) if ($PageFileInfo.MaximumSize -ne 0)
{ {
$hasSizes = $TRUE $hasSizes = $TRUE
$sizes += $PageFileInfo.MaximumSize $sizes += $PageFileInfo.MaximumSize
} }
} }
else else
{ {
for ($i = 0; $i -le $PageFileCount; $i++) for ($i = 0; $i -le $PageFileCount; $i++)
{ {
$files += $PageFileInfo[$i].Name $files += $PageFileInfo[$i].Name
if ($PageFileInfo[$i].MaximumSize -ne 0) if ($PageFileInfo[$i].MaximumSize -ne 0)
{ {
$hasSizes = $TRUE $hasSizes = $TRUE
$sizes += $PageFileInfo[$i].MaximumSize $sizes += $PageFileInfo[$i].MaximumSize
} }
} }
} }
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "" echo ""
echo " WARNING! $PageFileCount swap file(s) detected" echo " WARNING! $PageFileCount swap file(s) detected"
for ($i = 0; $i -lt $PageFileCount; $i++) for ($i = 0; $i -lt $PageFileCount; $i++)
{ {
$toPrint = " Name: " + $files[$i] $toPrint = " Name: " + $files[$i]
if ($hasSizes) if ($hasSizes)
{ {
$toPrint = $toPrint + " Size: " + $sizes[$i] $toPrint = $toPrint + " Size: " + $sizes[$i]
$toPrint = $toPrint -replace [Environment]::NewLine, "" $toPrint = $toPrint -replace [Environment]::NewLine, ""
} }
echo $toPrint echo $toPrint
} }
echo " It is recommended that you disable swap when running Cassandra" echo " It is recommended that you disable swap when running Cassandra"
echo " for performance and stability reasons." echo " for performance and stability reasons."
echo "" echo ""
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
} }
} }
# Validate that we need to run this function and that our config is good # Validate that we need to run this function and that our config is good
if ($env:MAX_HEAP_SIZE -and $env:HEAP_NEWSIZE) if ($env:MAX_HEAP_SIZE -and $env:HEAP_NEWSIZE)
{ {
return return
} }
if ((($env:MAX_HEAP_SIZE -and !$env:HEAP_NEWSIZE) -or (!$env:MAX_HEAP_SIZE -and $env:HEAP_NEWSIZE)) -and ($using_cms -eq $true)) if ((($env:MAX_HEAP_SIZE -and !$env:HEAP_NEWSIZE) -or (!$env:MAX_HEAP_SIZE -and $env:HEAP_NEWSIZE)) -and ($using_cms -eq $true))
{ {
echo "Please set or unset MAX_HEAP_SIZE and HEAP_NEWSIZE in pairs. Aborting startup." echo "Please set or unset MAX_HEAP_SIZE and HEAP_NEWSIZE in pairs. Aborting startup."
exit 1 exit 1
} }
$memObject = Get-WMIObject -class win32_physicalmemory $memObject = Get-WMIObject -class win32_physicalmemory
if ($memObject -eq $null) if ($memObject -eq $null)
{ {
echo "WARNING! Could not determine system memory. Defaulting to 2G heap, 512M newgen. Manually override in conf\jvm.options for different heap values." echo "WARNING! Could not determine system memory. Defaulting to 2G heap, 512M newgen. Manually override in conf\jvm.options for different heap values."
$env:MAX_HEAP_SIZE = "2048M" $env:MAX_HEAP_SIZE = "2048M"
$env:HEAP_NEWSIZE = "512M" $env:HEAP_NEWSIZE = "512M"
return return
} }
$memory = ($memObject | Measure-Object Capacity -Sum).sum $memory = ($memObject | Measure-Object Capacity -Sum).sum
$memoryMB = [Math]::Truncate($memory / (1024*1024)) $memoryMB = [Math]::Truncate($memory / (1024*1024))
$cpu = gwmi Win32_ComputerSystem | Select-Object NumberOfLogicalProcessors $cpu = gwmi Win32_ComputerSystem | Select-Object NumberOfLogicalProcessors
$systemCores = $cpu.NumberOfLogicalProcessors $systemCores = $cpu.NumberOfLogicalProcessors
# set max heap size based on the following # set max heap size based on the following
# max(min(1/2 ram, 1024MB), min(1/4 ram, 8GB)) # max(min(1/2 ram, 1024MB), min(1/4 ram, 8GB))
# calculate 1/2 ram and cap to 1024MB # calculate 1/2 ram and cap to 1024MB
# calculate 1/4 ram and cap to 8192MB # calculate 1/4 ram and cap to 8192MB
# pick the max # pick the max
$halfMem = [Math]::Truncate($memoryMB / 2) $halfMem = [Math]::Truncate($memoryMB / 2)
$quarterMem = [Math]::Truncate($halfMem / 2) $quarterMem = [Math]::Truncate($halfMem / 2)
if ($halfMem -gt 1024) if ($halfMem -gt 1024)
{ {
$halfMem = 1024 $halfMem = 1024
} }
if ($quarterMem -gt 8192) if ($quarterMem -gt 8192)
{ {
$quarterMem = 8192 $quarterMem = 8192
} }
$maxHeapMB = "" $maxHeapMB = ""
if ($halfMem -gt $quarterMem) if ($halfMem -gt $quarterMem)
{ {
$maxHeapMB = $halfMem $maxHeapMB = $halfMem
} }
else else
{ {
$maxHeapMB = $quarterMem $maxHeapMB = $quarterMem
} }
$env:MAX_HEAP_SIZE = [System.Convert]::ToString($maxHeapMB) + "M" $env:MAX_HEAP_SIZE = [System.Convert]::ToString($maxHeapMB) + "M"
# Young gen: min(max_sensible_per_modern_cpu_core * num_cores, 1/4 # Young gen: min(max_sensible_per_modern_cpu_core * num_cores, 1/4
$maxYGPerCore = 100 $maxYGPerCore = 100
$maxYGTotal = $maxYGPerCore * $systemCores $maxYGTotal = $maxYGPerCore * $systemCores
$desiredYG = [Math]::Truncate($maxHeapMB / 4) $desiredYG = [Math]::Truncate($maxHeapMB / 4)
if ($desiredYG -gt $maxYGTotal) if ($desiredYG -gt $maxYGTotal)
{ {
$env:HEAP_NEWSIZE = [System.Convert]::ToString($maxYGTotal) + "M" $env:HEAP_NEWSIZE = [System.Convert]::ToString($maxYGTotal) + "M"
} }
else else
{ {
$env:HEAP_NEWSIZE = [System.Convert]::ToString($desiredYG) + "M" $env:HEAP_NEWSIZE = [System.Convert]::ToString($desiredYG) + "M"
} }
} }
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function ParseJVMInfo Function ParseJVMInfo
{ {
# grab info about the JVM # grab info about the JVM
$pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "$env:JAVA_BIN" $pinfo.FileName = "$env:JAVA_BIN"
$pinfo.RedirectStandardError = $true $pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true $pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false $pinfo.UseShellExecute = $false
$pinfo.Arguments = "-d64 -version" $pinfo.Arguments = "-d64 -version"
$p = New-Object System.Diagnostics.Process $p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo $p.StartInfo = $pinfo
$p.Start() | Out-Null $p.Start() | Out-Null
$p.WaitForExit() $p.WaitForExit()
$stderr = $p.StandardError.ReadToEnd() $stderr = $p.StandardError.ReadToEnd()
$env:JVM_ARCH = "64-bit" $env:JVM_ARCH = "64-bit"
if ($stderr.Contains("Error")) if ($stderr.Contains("Error"))
{ {
# 32-bit JVM. re-run w/out -d64 # 32-bit JVM. re-run w/out -d64
echo "Failed 64-bit check. Re-running to get version from 32-bit" echo "Failed 64-bit check. Re-running to get version from 32-bit"
$pinfo.Arguments = "-version" $pinfo.Arguments = "-version"
$p = New-Object System.Diagnostics.Process $p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo $p.StartInfo = $pinfo
$p.Start() | Out-Null $p.Start() | Out-Null
$p.WaitForExit() $p.WaitForExit()
$stderr = $p.StandardError.ReadToEnd() $stderr = $p.StandardError.ReadToEnd()
$env:JVM_ARCH = "32-bit" $env:JVM_ARCH = "32-bit"
} }
$sa = $stderr.Split("""") $sa = $stderr.Split("""")
$env:JVM_VERSION = $sa[1] $env:JVM_VERSION = $sa[1]
if ($stderr.Contains("OpenJDK")) if ($stderr.Contains("OpenJDK"))
{ {
$env:JVM_VENDOR = "OpenJDK" $env:JVM_VENDOR = "OpenJDK"
} }
elseif ($stderr.Contains("Java(TM)")) elseif ($stderr.Contains("Java(TM)"))
{ {
$env:JVM_VENDOR = "Oracle" $env:JVM_VENDOR = "Oracle"
} }
else else
{ {
$JVM_VENDOR = "other" $JVM_VENDOR = "other"
} }
$pa = $sa[1].Split("_") $pa = $sa[1].Split("_")
$subVersion = $pa[1] $subVersion = $pa[1]
# Deal with -b (build) versions # Deal with -b (build) versions
if ($subVersion -contains '-') if ($subVersion -contains '-')
{ {
$patchAndBuild = $subVersion.Split("-") $patchAndBuild = $subVersion.Split("-")
$subVersion = $patchAndBuild[0] $subVersion = $patchAndBuild[0]
} }
$env:JVM_PATCH_VERSION = $subVersion $env:JVM_PATCH_VERSION = $subVersion
} }
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Function SetCassandraEnvironment Function SetCassandraEnvironment
{ {
if (Test-Path Env:\JAVA_HOME) if (Test-Path Env:\JAVA_HOME)
{ {
$env:JAVA_BIN = "$env:JAVA_HOME\bin\java.exe" $env:JAVA_BIN = "$env:JAVA_HOME\bin\java.exe"
} }
elseif (Get-Command "java.exe") elseif (Get-Command "java.exe")
{ {
$env:JAVA_BIN = "java.exe" $env:JAVA_BIN = "java.exe"
} }
else else
{ {
echo "ERROR! No JAVA_HOME set and could not find java.exe in the path." echo "ERROR! No JAVA_HOME set and could not find java.exe in the path."
exit exit
} }
SetCassandraHome SetCassandraHome
$env:CASSANDRA_CONF = "$env:CASSANDRA_HOME\conf" $env:CASSANDRA_CONF = "$env:CASSANDRA_HOME\conf"
$env:CASSANDRA_PARAMS="-Dcassandra -Dlogback.configurationFile=logback.xml" $env:CASSANDRA_PARAMS="-Dcassandra -Dlogback.configurationFile=logback.xml"
$logdir = "$env:CASSANDRA_HOME\logs" $logdir = "$env:CASSANDRA_HOME\logs"
$storagedir = "$env:CASSANDRA_HOME\data" $storagedir = "$env:CASSANDRA_HOME\data"
$env:CASSANDRA_PARAMS = $env:CASSANDRA_PARAMS + " -Dcassandra.logdir=""$logdir"" -Dcassandra.storagedir=""$storagedir""" $env:CASSANDRA_PARAMS = $env:CASSANDRA_PARAMS + " -Dcassandra.logdir=""$logdir"" -Dcassandra.storagedir=""$storagedir"""
SetCassandraMain SetCassandraMain
BuildClassPath BuildClassPath
# Override these to set the amount of memory to allocate to the JVM at # Override these to set the amount of memory to allocate to the JVM at
# start-up. For production use you may wish to adjust this for your # start-up. For production use you may wish to adjust this for your
# environment. MAX_HEAP_SIZE is the total amount of memory dedicated # environment. MAX_HEAP_SIZE is the total amount of memory dedicated
# to the Java heap. HEAP_NEWSIZE refers to the size of the young # to the Java heap. HEAP_NEWSIZE refers to the size of the young
# generation. Both MAX_HEAP_SIZE and HEAP_NEWSIZE should be either set # generation. Both MAX_HEAP_SIZE and HEAP_NEWSIZE should be either set
# or not (if you set one, set the other). # or not (if you set one, set the other).
# #
# The main trade-off for the young generation is that the larger it # The main trade-off for the young generation is that the larger it
# is, the longer GC pause times will be. The shorter it is, the more # is, the longer GC pause times will be. The shorter it is, the more
# expensive GC will be (usually). # expensive GC will be (usually).
# #
# The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent # The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent
# times. If in doubt, and if you do not particularly want to tweak, go # times. If in doubt, and if you do not particularly want to tweak, go
# 100 MB per physical CPU core. # 100 MB per physical CPU core.
#GC log path has to be defined here since it needs to find CASSANDRA_HOME #GC log path has to be defined here since it needs to find CASSANDRA_HOME
$env:JVM_OPTS="$env:JVM_OPTS -Xloggc:""$env:CASSANDRA_HOME/logs/gc.log""" $env:JVM_OPTS="$env:JVM_OPTS -Xloggc:""$env:CASSANDRA_HOME/logs/gc.log"""
# Read user-defined JVM options from jvm.options file # Read user-defined JVM options from jvm.options file
$content = Get-Content "$env:CASSANDRA_CONF\jvm.options" $content = Get-Content "$env:CASSANDRA_CONF\jvm.options"
for ($i = 0; $i -lt $content.Count; $i++) for ($i = 0; $i -lt $content.Count; $i++)
{ {
$line = $content[$i] $line = $content[$i]
if ($line.StartsWith("-")) if ($line.StartsWith("-"))
{ {
$env:JVM_OPTS = "$env:JVM_OPTS $line" $env:JVM_OPTS = "$env:JVM_OPTS $line"
} }
} }
$defined_xmn = $env:JVM_OPTS -like '*Xmn*' $defined_xmn = $env:JVM_OPTS -like '*Xmn*'
$defined_xmx = $env:JVM_OPTS -like '*Xmx*' $defined_xmx = $env:JVM_OPTS -like '*Xmx*'
$defined_xms = $env:JVM_OPTS -like '*Xms*' $defined_xms = $env:JVM_OPTS -like '*Xms*'
$using_cms = $env:JVM_OPTS -like '*UseConcMarkSweepGC*' $using_cms = $env:JVM_OPTS -like '*UseConcMarkSweepGC*'
#$env:MAX_HEAP_SIZE="4096M" #$env:MAX_HEAP_SIZE="4096M"
#$env:HEAP_NEWSIZE="800M" #$env:HEAP_NEWSIZE="800M"
CalculateHeapSizes CalculateHeapSizes
ParseJVMInfo ParseJVMInfo
# We only set -Xms and -Xmx if they were not defined on jvm.options file # We only set -Xms and -Xmx if they were not defined on jvm.options file
# If defined, both Xmx and Xms should be defined together. # If defined, both Xmx and Xms should be defined together.
if (($defined_xmx -eq $false) -and ($defined_xms -eq $false)) if (($defined_xmx -eq $false) -and ($defined_xms -eq $false))
{ {
$env:JVM_OPTS="$env:JVM_OPTS -Xms$env:MAX_HEAP_SIZE" $env:JVM_OPTS="$env:JVM_OPTS -Xms$env:MAX_HEAP_SIZE"
$env:JVM_OPTS="$env:JVM_OPTS -Xmx$env:MAX_HEAP_SIZE" $env:JVM_OPTS="$env:JVM_OPTS -Xmx$env:MAX_HEAP_SIZE"
} }
elseif (($defined_xmx -eq $false) -or ($defined_xms -eq $false)) elseif (($defined_xmx -eq $false) -or ($defined_xms -eq $false))
{ {
echo "Please set or unset -Xmx and -Xms flags in pairs on jvm.options file." echo "Please set or unset -Xmx and -Xms flags in pairs on jvm.options file."
exit exit
} }
# We only set -Xmn flag if it was not defined in jvm.options file # We only set -Xmn flag if it was not defined in jvm.options file
# and if the CMS GC is being used # and if the CMS GC is being used
# If defined, both Xmn and Xmx should be defined together. # If defined, both Xmn and Xmx should be defined together.
if (($defined_xmn -eq $true) -and ($defined_xmx -eq $false)) if (($defined_xmn -eq $true) -and ($defined_xmx -eq $false))
{ {
echo "Please set or unset -Xmx and -Xmn flags in pairs on jvm.options file." echo "Please set or unset -Xmx and -Xmn flags in pairs on jvm.options file."
exit exit
} }
elseif (($defined_xmn -eq $false) -and ($using_cms -eq $true)) elseif (($defined_xmn -eq $false) -and ($using_cms -eq $true))
{ {
$env:JVM_OPTS="$env:JVM_OPTS -Xmn$env:HEAP_NEWSIZE" $env:JVM_OPTS="$env:JVM_OPTS -Xmn$env:HEAP_NEWSIZE"
} }
if (($env:JVM_ARCH -eq "64-Bit") -and ($using_cms -eq $true)) if (($env:JVM_ARCH -eq "64-Bit") -and ($using_cms -eq $true))
{ {
$env:JVM_OPTS="$env:JVM_OPTS -XX:+UseCondCardMark" $env:JVM_OPTS="$env:JVM_OPTS -XX:+UseCondCardMark"
} }
# Add sigar env - see Cassandra-7838 # Add sigar env - see Cassandra-7838
$env:JVM_OPTS = "$env:JVM_OPTS -Djava.library.path=""$env:CASSANDRA_HOME\lib\sigar-bin""" $env:JVM_OPTS = "$env:JVM_OPTS -Djava.library.path=""$env:CASSANDRA_HOME\lib\sigar-bin"""
# Confirm we're on high performance power plan, warn if not # Confirm we're on high performance power plan, warn if not
# Change to $true to suppress this warning # Change to $true to suppress this warning
$suppressPowerWarning = $false $suppressPowerWarning = $false
if (!$suppressPowerWarning) if (!$suppressPowerWarning)
{ {
$currentProfile = powercfg /GETACTIVESCHEME $currentProfile = powercfg /GETACTIVESCHEME
if (!$currentProfile.Contains("High performance")) if (!$currentProfile.Contains("High performance"))
{ {
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "" echo ""
echo " WARNING! Detected a power profile other than High Performance." echo " WARNING! Detected a power profile other than High Performance."
echo " Performance of this node will suffer." echo " Performance of this node will suffer."
echo " Modify conf\cassandra.env.ps1 to suppress this warning." echo " Modify conf\cassandra.env.ps1 to suppress this warning."
echo "" echo ""
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
echo "*---------------------------------------------------------------------*" echo "*---------------------------------------------------------------------*"
} }
} }
# provides hints to the JIT compiler # provides hints to the JIT compiler
$env:JVM_OPTS = "$env:JVM_OPTS -XX:CompileCommandFile=$env:CASSANDRA_CONF\hotspot_compiler" $env:JVM_OPTS = "$env:JVM_OPTS -XX:CompileCommandFile=$env:CASSANDRA_CONF\hotspot_compiler"
# add the jamm javaagent # add the jamm javaagent
if (($env:JVM_VENDOR -ne "OpenJDK") -or ($env:JVM_VERSION.CompareTo("1.6.0") -eq 1) -or if (($env:JVM_VENDOR -ne "OpenJDK") -or ($env:JVM_VERSION.CompareTo("1.6.0") -eq 1) -or
(($env:JVM_VERSION -eq "1.6.0") -and ($env:JVM_PATCH_VERSION.CompareTo("22") -eq 1))) (($env:JVM_VERSION -eq "1.6.0") -and ($env:JVM_PATCH_VERSION.CompareTo("22") -eq 1)))
{ {
$env:JVM_OPTS = "$env:JVM_OPTS -javaagent:""$env:CASSANDRA_HOME\lib\jamm-0.3.0.jar""" $env:JVM_OPTS = "$env:JVM_OPTS -javaagent:""$env:CASSANDRA_HOME\lib\jamm-0.3.0.jar"""
} }
# set jvm HeapDumpPath with CASSANDRA_HEAPDUMP_DIR # set jvm HeapDumpPath with CASSANDRA_HEAPDUMP_DIR
if ($env:CASSANDRA_HEAPDUMP_DIR) if ($env:CASSANDRA_HEAPDUMP_DIR)
{ {
@ -393,88 +393,98 @@ Function SetCassandraEnvironment
$env:JVM_OPTS="$env:JVM_OPTS -XX:HeapDumpPath=$env:CASSANDRA_HEAPDUMP_DIR\cassandra-$unixTimestamp-pid$pid.hprof" $env:JVM_OPTS="$env:JVM_OPTS -XX:HeapDumpPath=$env:CASSANDRA_HEAPDUMP_DIR\cassandra-$unixTimestamp-pid$pid.hprof"
} }
if ($env:JVM_VERSION.CompareTo("1.8.0") -eq -1 -or [convert]::ToInt32($env:JVM_PATCH_VERSION) -lt 40) if ($env:JVM_VERSION.CompareTo("1.8.0") -eq -1 -or [convert]::ToInt32($env:JVM_PATCH_VERSION) -lt 40)
{ {
echo "Cassandra 3.0 and later require Java 8u40 or later." echo "Cassandra 3.0 and later require Java 8u40 or later."
exit exit
} }
# enable assertions. disabling this in production will give a modest # enable assertions. disabling this in production will give a modest
# performance benefit (around 5%). # performance benefit (around 5%).
$env:JVM_OPTS = "$env:JVM_OPTS -ea" $env:JVM_OPTS = "$env:JVM_OPTS -ea"
# Specifies the default port over which Cassandra will be available for # Specifies the default port over which Cassandra will be available for
# JMX connections. # JMX connections.
$JMX_PORT="7199" $JMX_PORT="7199"
# store in env to check if it's avail in verification # store in env to check if it's avail in verification
$env:JMX_PORT=$JMX_PORT $env:JMX_PORT=$JMX_PORT
# enable thread priorities, primarily so we can give periodic tasks # enable thread priorities, primarily so we can give periodic tasks
# a lower priority to avoid interfering with client workload # a lower priority to avoid interfering with client workload
$env:JVM_OPTS="$env:JVM_OPTS -XX:+UseThreadPriorities" $env:JVM_OPTS="$env:JVM_OPTS -XX:+UseThreadPriorities"
# allows lowering thread priority without being root on linux - probably # allows lowering thread priority without being root on linux - probably
# not necessary on Windows but doesn't harm anything. # not necessary on Windows but doesn't harm anything.
# see http://tech.stolsvik.com/2010/01/linux-java-thread-priorities-workar # see http://tech.stolsvik.com/2010/01/linux-java-thread-priorities-workar
$env:JVM_OPTS="$env:JVM_OPTS -XX:ThreadPriorityPolicy=42" $env:JVM_OPTS="$env:JVM_OPTS -XX:ThreadPriorityPolicy=42"
$env:JVM_OPTS="$env:JVM_OPTS -XX:+HeapDumpOnOutOfMemoryError" $env:JVM_OPTS="$env:JVM_OPTS -XX:+HeapDumpOnOutOfMemoryError"
# Per-thread stack size. # stop the jvm on OutOfMemoryError as it can result in some data corruption
$env:JVM_OPTS="$env:JVM_OPTS -Xss256k" # uncomment the preferred option
# ExitOnOutOfMemoryError and CrashOnOutOfMemoryError require a JRE greater or equals to 1.7 update 101 or 1.8 update 92
# Larger interned string table, for gossip's benefit (CASSANDRA-6410) # $env:JVM_OPTS="$env:JVM_OPTS -XX:+ExitOnOutOfMemoryError"
$env:JVM_OPTS="$env:JVM_OPTS -XX:StringTableSize=1000003" # $env:JVM_OPTS="$env:JVM_OPTS -XX:+CrashOnOutOfMemoryError"
$env:JVM_OPTS="$env:JVM_OPTS -XX:OnOutOfMemoryError=""taskkill /F /PID %p"""
# Make sure all memory is faulted and zeroed on startup.
# This helps prevent soft faults in containers and makes # print an heap histogram on OutOfMemoryError
# transparent hugepage allocation more effective. # $env:JVM_OPTS="$env:JVM_OPTS -Dcassandra.printHeapHistogramOnOutOfMemoryError=true"
#$env:JVM_OPTS="$env:JVM_OPTS -XX:+AlwaysPreTouch"
# Per-thread stack size.
# Biased locking does not benefit Cassandra. $env:JVM_OPTS="$env:JVM_OPTS -Xss256k"
$env:JVM_OPTS="$env:JVM_OPTS -XX:-UseBiasedLocking"
# Larger interned string table, for gossip's benefit (CASSANDRA-6410)
# Enable thread-local allocation blocks and allow the JVM to automatically $env:JVM_OPTS="$env:JVM_OPTS -XX:StringTableSize=1000003"
# resize them at runtime.
$env:JVM_OPTS="$env:JVM_OPTS -XX:+UseTLAB -XX:+ResizeTLAB" # Make sure all memory is faulted and zeroed on startup.
# This helps prevent soft faults in containers and makes
# http://www.evanjones.ca/jvm-mmap-pause.html # transparent hugepage allocation more effective.
$env:JVM_OPTS="$env:JVM_OPTS -XX:+PerfDisableSharedMem" #$env:JVM_OPTS="$env:JVM_OPTS -XX:+AlwaysPreTouch"
# Configure the following for JEMallocAllocator and if jemalloc is not available in the system # Biased locking does not benefit Cassandra.
# library path. $env:JVM_OPTS="$env:JVM_OPTS -XX:-UseBiasedLocking"
# set LD_LIBRARY_PATH=<JEMALLOC_HOME>/lib/
# $env:JVM_OPTS="$env:JVM_OPTS -Djava.library.path=<JEMALLOC_HOME>/lib/" # Enable thread-local allocation blocks and allow the JVM to automatically
# resize them at runtime.
# uncomment to have Cassandra JVM listen for remote debuggers/profilers on port 1414 $env:JVM_OPTS="$env:JVM_OPTS -XX:+UseTLAB -XX:+ResizeTLAB"
# $env:JVM_OPTS="$env:JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1414"
# http://www.evanjones.ca/jvm-mmap-pause.html
# Prefer binding to IPv4 network intefaces (when net.ipv6.bindv6only=1). See $env:JVM_OPTS="$env:JVM_OPTS -XX:+PerfDisableSharedMem"
# http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342561 (short version:
# comment out this entry to enable IPv6 support). # Configure the following for JEMallocAllocator and if jemalloc is not available in the system
$env:JVM_OPTS="$env:JVM_OPTS -Djava.net.preferIPv4Stack=true" # library path.
# set LD_LIBRARY_PATH=<JEMALLOC_HOME>/lib/
# jmx: metrics and administration interface # $env:JVM_OPTS="$env:JVM_OPTS -Djava.library.path=<JEMALLOC_HOME>/lib/"
#
# add this if you're having trouble connecting: # uncomment to have Cassandra JVM listen for remote debuggers/profilers on port 1414
# $env:JVM_OPTS="$env:JVM_OPTS -Djava.rmi.server.hostname=<public name>" # $env:JVM_OPTS="$env:JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1414"
#
# see # Prefer binding to IPv4 network intefaces (when net.ipv6.bindv6only=1). See
# https://blogs.oracle.com/jmxetc/entry/troubleshooting_connection_problems_in_jconsole # http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342561 (short version:
# for more on configuring JMX through firewalls, etc. (Short version: # comment out this entry to enable IPv6 support).
# get it working with no firewall first.) $env:JVM_OPTS="$env:JVM_OPTS -Djava.net.preferIPv4Stack=true"
#
# Due to potential security exploits, Cassandra ships with JMX accessible # jmx: metrics and administration interface
# *only* from localhost. To enable remote JMX connections, uncomment lines below #
# with authentication and ssl enabled. See https://wiki.apache.org/cassandra/JmxSecurity # add this if you're having trouble connecting:
# # $env:JVM_OPTS="$env:JVM_OPTS -Djava.rmi.server.hostname=<public name>"
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT" #
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false" # see
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true" # https://blogs.oracle.com/jmxetc/entry/troubleshooting_connection_problems_in_jconsole
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.password.file=C:/jmxremote.password" # for more on configuring JMX through firewalls, etc. (Short version:
$env:JVM_OPTS="$env:JVM_OPTS -Dcassandra.jmx.local.port=$JMX_PORT -XX:+DisableExplicitGC" # get it working with no firewall first.)
#
$env:JVM_OPTS="$env:JVM_OPTS $env:JVM_EXTRA_OPTS" # Due to potential security exploits, Cassandra ships with JMX accessible
# *only* from localhost. To enable remote JMX connections, uncomment lines below
#$env:JVM_OPTS="$env:JVM_OPTS -XX:+UnlockCommercialFeatures -XX:+FlightRecorder" # with authentication and ssl enabled. See https://wiki.apache.org/cassandra/JmxSecurity
} #
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT"
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false"
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
#$env:JVM_OPTS="$env:JVM_OPTS -Dcom.sun.management.jmxremote.password.file=C:/jmxremote.password"
$env:JVM_OPTS="$env:JVM_OPTS -Dcassandra.jmx.local.port=$JMX_PORT -XX:+DisableExplicitGC"
$env:JVM_OPTS="$env:JVM_OPTS $env:JVM_EXTRA_OPTS"
#$env:JVM_OPTS="$env:JVM_OPTS -XX:+UnlockCommercialFeatures -XX:+FlightRecorder"
}

View File

@ -247,6 +247,18 @@ if [ "x$CASSANDRA_HEAPDUMP_DIR" != "x" ]; then
JVM_OPTS="$JVM_OPTS -XX:HeapDumpPath=$CASSANDRA_HEAPDUMP_DIR/cassandra-`date +%s`-pid$$.hprof" JVM_OPTS="$JVM_OPTS -XX:HeapDumpPath=$CASSANDRA_HEAPDUMP_DIR/cassandra-`date +%s`-pid$$.hprof"
fi fi
# stop the jvm on OutOfMemoryError as it can result in some data corruption
# uncomment the preferred option
# ExitOnOutOfMemoryError and CrashOnOutOfMemoryError require a JRE greater or equals to 1.7 update 101 or 1.8 update 92
# For OnOutOfMemoryError we cannot use the JVM_OPTS variables because bash commands split words
# on white spaces without taking quotes into account
# JVM_OPTS="$JVM_OPTS -XX:+ExitOnOutOfMemoryError"
# JVM_OPTS="$JVM_OPTS -XX:+CrashOnOutOfMemoryError"
JVM_ON_OUT_OF_MEMORY_ERROR_OPT="-XX:OnOutOfMemoryError=kill -9 %p"
# print an heap histogram on OutOfMemoryError
# JVM_OPTS="$JVM_OPTS -Dcassandra.printHeapHistogramOnOutOfMemoryError=true"
# uncomment to have Cassandra JVM listen for remote debuggers/profilers on port 1414 # uncomment to have Cassandra JVM listen for remote debuggers/profilers on port 1414
# JVM_OPTS="$JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1414" # JVM_OPTS="$JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1414"

View File

@ -20,6 +20,8 @@ package org.apache.cassandra.service;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.*; import java.util.*;
@ -189,6 +191,78 @@ public class StartupChecks
{ {
logger.warn("Non-Oracle JVM detected. Some features, such as immediate unmap of compacted SSTables, may not work as intended"); logger.warn("Non-Oracle JVM detected. Some features, such as immediate unmap of compacted SSTables, may not work as intended");
} }
else
{
checkOutOfMemoryHandling();
}
}
/**
* Checks that the JVM is configured to handle OutOfMemoryError
*/
private void checkOutOfMemoryHandling()
{
int version = getJavaVersion();
int update = getUpdate();
// The ExitOnOutOfMemory and CrashOnOutOfMemory are supported since the version 7u101 and 8u92
boolean jreSupportExitOnOutOfMemory = version > 8
|| (version == 7 && update >= 101)
|| (version == 8 && update >= 92);
if (jreSupportExitOnOutOfMemory)
{
if (!jvmOptionsContainsOneOf("-XX:OnOutOfMemoryError=", "-XX:+ExitOnOutOfMemoryError", "-XX:+CrashOnOutOfMemoryError"))
logger.warn("The JVM is not configured to stop on OutOfMemoryError which can cause data corruption."
+ " Use one of the following JVM options to configure the behavior on OutOfMemoryError: "
+ " -XX:+ExitOnOutOfMemoryError, -XX:+CrashOnOutOfMemoryError, or -XX:OnOutOfMemoryError=\"<cmd args>;<cmd args>\"");
}
else
{
if (!jvmOptionsContainsOneOf("-XX:OnOutOfMemoryError="))
logger.warn("The JVM is not configured to stop on OutOfMemoryError which can cause data corruption."
+ " Either upgrade your JRE to a version greater or equal to 8u92 and use -XX:+ExitOnOutOfMemoryError/-XX:+CrashOnOutOfMemoryError"
+ " or use -XX:OnOutOfMemoryError=\"<cmd args>;<cmd args>\" on your current JRE.");
}
}
/**
* Returns the java version number for an Oracle JVM.
* @return the java version number
*/
private int getJavaVersion()
{
String jreVersion = System.getProperty("java.version");
String version = jreVersion.startsWith("1.") ? jreVersion.substring(2, 3) // Pre 9 version
: jreVersion.substring(0, jreVersion.indexOf('.'));
return Integer.parseInt(version);
}
/**
* Return the update number for an Oracle JVM.
* @return the update number
*/
private int getUpdate()
{
String jreVersion = System.getProperty("java.version");
int updateSeparatorIndex = jreVersion.indexOf('_');
return Integer.parseInt(jreVersion.substring(updateSeparatorIndex + 1));
}
/**
* Checks if one of the specified options is being used.
* @param optionNames The name of the options to check
* @return {@code true} if one of the specified options is being used, {@code false} otherwise.
*/
private boolean jvmOptionsContainsOneOf(String... optionNames)
{
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> inputArguments = runtimeMxBean.getInputArguments();
for (String argument : inputArguments)
{
for (String optionName : optionNames)
if (argument.startsWith(optionName))
return true;
}
return false;
} }
}; };

View File

@ -19,11 +19,6 @@ package org.apache.cassandra.utils;
import java.io.*; import java.io.*;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.text.StrBuilder; import org.apache.commons.lang3.text.StrBuilder;
@ -32,7 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Utility to generate heap dumps. * Utility to log heap histogram.
* *
*/ */
public final class HeapUtils public final class HeapUtils
@ -40,65 +35,43 @@ public final class HeapUtils
private static final Logger logger = LoggerFactory.getLogger(HeapUtils.class); private static final Logger logger = LoggerFactory.getLogger(HeapUtils.class);
/** /**
* Generates a HEAP dump in the directory specified by the <code>HeapDumpPath</code> JVM option * Generates a HEAP histogram in the log file.
* or in the <code>CASSANDRA_HOME</code> directory.
*/ */
public static void generateHeapDump() public static void logHeapHistogram()
{ {
Long processId = getProcessId(); try
if (processId == null)
{ {
logger.error("The process ID could not be retrieved. Skipping heap dump generation."); logger.info("Trying to log the heap histogram using jcmd");
return;
}
String heapDumpPath = getHeapDumpPathOption(); Long processId = getProcessId();
if (heapDumpPath == null) if (processId == null)
{
String cassandraHome = System.getenv("CASSANDRA_HOME");
if (cassandraHome == null)
{ {
logger.error("The process ID could not be retrieved. Skipping heap histogram generation.");
return; return;
} }
heapDumpPath = cassandraHome; String jcmdPath = getJcmdPath();
}
Path dumpPath = FileSystems.getDefault().getPath(heapDumpPath); // The jcmd file could not be found. In this case let's default to jcmd in the hope that it is in the path.
if (Files.isDirectory(dumpPath)) String jcmdCommand = jcmdPath == null ? "jcmd" : jcmdPath;
{
dumpPath = dumpPath.resolve("java_pid" + processId + ".hprof");
}
String jmapPath = getJmapPath(); String[] histoCommands = new String[] {jcmdCommand,
processId.toString(),
"GC.class_histogram"};
// The jmap file could not be found. In this case let's default to jmap in the hope that it is in the path.
String jmapCommand = jmapPath == null ? "jmap" : jmapPath;
String[] dumpCommands = new String[] {jmapCommand,
"-dump:format=b,file=" + dumpPath,
processId.toString()};
// Lets also log the Heap histogram
String[] histoCommands = new String[] {jmapCommand,
"-histo",
processId.toString()};
try
{
logProcessOutput(Runtime.getRuntime().exec(dumpCommands));
logProcessOutput(Runtime.getRuntime().exec(histoCommands)); logProcessOutput(Runtime.getRuntime().exec(histoCommands));
} }
catch (IOException e) catch (Throwable e)
{ {
logger.error("The heap dump could not be generated due to the following error: ", e); logger.error("The heap histogram could not be generated due to the following error: ", e);
} }
} }
/** /**
* Retrieve the path to the JMAP executable. * Retrieve the path to the JCMD executable.
* @return the path to the JMAP executable or null if it cannot be found. * @return the path to the JCMD executable or null if it cannot be found.
*/ */
private static String getJmapPath() private static String getJcmdPath()
{ {
// Searching in the JAVA_HOME is safer than searching into System.getProperty("java.home") as the Oracle // Searching in the JAVA_HOME is safer than searching into System.getProperty("java.home") as the Oracle
// JVM might use the JRE which do not contains jmap. // JVM might use the JRE which do not contains jmap.
@ -111,7 +84,7 @@ public final class HeapUtils
{ {
public boolean accept(File dir, String name) public boolean accept(File dir, String name)
{ {
return name.startsWith("jmap"); return name.startsWith("jcmd");
} }
}); });
return ArrayUtils.isEmpty(files) ? null : files[0].getPath(); return ArrayUtils.isEmpty(files) ? null : files[0].getPath();
@ -136,32 +109,6 @@ public final class HeapUtils
logger.info(builder.toString()); logger.info(builder.toString());
} }
/**
* Retrieves the value of the <code>HeapDumpPath</code> JVM option.
* @return the value of the <code>HeapDumpPath</code> JVM option or <code>null</code> if the value has not been
* specified.
*/
private static String getHeapDumpPathOption()
{
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> inputArguments = runtimeMxBean.getInputArguments();
String heapDumpPathOption = null;
for (String argument : inputArguments)
{
if (argument.startsWith("-XX:HeapDumpPath="))
{
heapDumpPathOption = argument;
// We do not break in case the option has been specified several times.
// In general it seems that JVMs use the right-most argument as the winner.
}
}
if (heapDumpPathOption == null)
return null;
return heapDumpPathOption.substring(17, heapDumpPathOption.length());
}
/** /**
* Retrieves the process ID or <code>null</code> if the process ID cannot be retrieved. * Retrieves the process ID or <code>null</code> if the process ID cannot be retrieved.
* @return the process ID or <code>null</code> if the process ID cannot be retrieved. * @return the process ID or <code>null</code> if the process ID cannot be retrieved.

View File

@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,6 +42,8 @@ public final class JVMStabilityInspector
private static final Logger logger = LoggerFactory.getLogger(JVMStabilityInspector.class); private static final Logger logger = LoggerFactory.getLogger(JVMStabilityInspector.class);
private static Killer killer = new Killer(); private static Killer killer = new Killer();
private static Object lock = new Object();
private static boolean printingHeapHistogram;
private JVMStabilityInspector() {} private JVMStabilityInspector() {}
@ -55,8 +58,25 @@ public final class JVMStabilityInspector
boolean isUnstable = false; boolean isUnstable = false;
if (t instanceof OutOfMemoryError) if (t instanceof OutOfMemoryError)
{ {
isUnstable = true; if (Boolean.getBoolean("cassandra.printHeapHistogramOnOutOfMemoryError"))
HeapUtils.generateHeapDump(); {
// We want to avoid printing multiple time the heap histogram if multiple OOM errors happen in a short
// time span.
synchronized(lock)
{
if (printingHeapHistogram)
return;
printingHeapHistogram = true;
}
HeapUtils.logHeapHistogram();
}
logger.error("OutOfMemory error letting the JVM handle the error:", t);
StorageService.instance.removeShutdownHook();
// We let the JVM handle the error. The startup checks should have warned the user if it did not configure
// the JVM behavior in case of OOM (CASSANDRA-13006).
throw (OutOfMemoryError) t;
} }
if (DatabaseDescriptor.getDiskFailurePolicy() == Config.DiskFailurePolicy.die) if (DatabaseDescriptor.getDiskFailurePolicy() == Config.DiskFailurePolicy.die)

View File

@ -27,8 +27,10 @@ import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.io.FSReadError; import org.apache.cassandra.io.FSReadError;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class JVMStabilityInspectorTest public class JVMStabilityInspectorTest
{ {
@ -46,10 +48,6 @@ public class JVMStabilityInspectorTest
JVMStabilityInspector.inspectThrowable(new IOException()); JVMStabilityInspector.inspectThrowable(new IOException());
assertFalse(killerForTests.wasKilled()); assertFalse(killerForTests.wasKilled());
killerForTests.reset();
JVMStabilityInspector.inspectThrowable(new OutOfMemoryError());
assertTrue(killerForTests.wasKilled());
DatabaseDescriptor.setDiskFailurePolicy(Config.DiskFailurePolicy.die); DatabaseDescriptor.setDiskFailurePolicy(Config.DiskFailurePolicy.die);
killerForTests.reset(); killerForTests.reset();
JVMStabilityInspector.inspectThrowable(new FSReadError(new IOException(), "blah")); JVMStabilityInspector.inspectThrowable(new FSReadError(new IOException(), "blah"));
@ -63,11 +61,6 @@ public class JVMStabilityInspectorTest
killerForTests.reset(); killerForTests.reset();
JVMStabilityInspector.inspectThrowable(new Exception(new IOException())); JVMStabilityInspector.inspectThrowable(new Exception(new IOException()));
assertFalse(killerForTests.wasKilled()); assertFalse(killerForTests.wasKilled());
killerForTests.reset();
JVMStabilityInspector.inspectThrowable(new Exception(new OutOfMemoryError()));
assertTrue(killerForTests.wasKilled());
} }
finally finally
{ {
@ -77,6 +70,23 @@ public class JVMStabilityInspectorTest
} }
} }
@Test
public void testOutOfMemoryHandling()
{
for (Throwable oom : asList(new OutOfMemoryError(), new Exception(new OutOfMemoryError())))
{
try
{
JVMStabilityInspector.inspectThrowable(oom);
fail("The JVMStabilityInspector should delegate the handling of OutOfMemoryErrors to the JVM");
}
catch (OutOfMemoryError e)
{
assertTrue(true);
}
}
}
@Test @Test
public void fileHandleTest() public void fileHandleTest()
{ {