JVM无法向虚拟机申请内存

一、问题背景

       在伪分布式的Hadoop安装中,hadoop-env.sh配置文件关于内存分配的项都是默认值,sbin/hadoop-daemon.sh start namenode可以,但是当sbin/hadoop-daemon.sh start datanode或者jps时失败。提示:

Error occurred during initialization of VM
Could not reserve enough space for the card marking array
或者
Error occurred during initialization of VM
Could not reserve enough space for object heap
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

 

 二、问题解决

   (1)尝试改变内存大小。但是hadoop都是使用的默认大小,按理说不应该需要改变。

     hadoop配置文件,hadoop-env.sh中有个选项HADOOP_NAMENODE_OPTS,此JVM选项是用来设置内存大小的。比如:HADOOP_NAMENODE_OPTS=-Xmx2000m。那么就是给namenode分配了2000MB的空间。

     如果改变了namenode的内存大小,那么secondarynamenode的内存的大小同样也要改变,其选项是HADOOP_SECONDARYNAMENODE_OPTS。

  (2)虚拟机内存分配策略

      Memory Overcommit的意思是操作系统承诺给进程的内存大小超过了实际可用的内存。一个保守的操作系统不会允许memory overcommit,有多少就分配多少,再申请就没有了,这其实有些浪费内存,因为进程实际使用到的内存往往比申请的内存要少,比如某个进程malloc()了200MB内存,但实际上只用到了100MB,按照UNIX/Linux的算法,物理内存页的分配发生在使用的瞬间,而不是在申请的瞬间,也就是说未用到的100MB内存根本就没有分配,这100MB内存就闲置了。下面这个概念很重要,是理解memory overcommit的关键:
     commit(或overcommit)针对的是内存申请,内存申请不等于内存分配,内存只在实际用到的时候才分配。

      Linux是允许memory overcommit的,只要你来申请内存我就给你,寄希望于进程实际上用不到那么多内存,但万一用到那么多了呢?那就会发生类似“银行挤兑”的危机,现金(内存)不足了。Linux设计了一个OOM killer机制(OOM = out-of-memory)来处理这种危机:挑选一个进程出来杀死,以腾出部分内存,如果还不够就继续杀…也可通过设置内核参数 vm.panic_on_oom 使得发生OOM时自动重启系统。这都是有风险的机制,重启有可能造成业务中断,杀死进程也有可能导致业务中断,我自己的这个小网站就碰到过这种问题,参见前文。所以Linux 2.6之后允许通过内核参数 vm.overcommit_memory 禁止memory overcommit。

     内核参数 vm.overcommit_memory 接受三种取值:

0 – Heuristic overcommit handling. 这是缺省值,它允许overcommit,但过于明目张胆
的 overcommit会被拒绝,比如 malloc一次性申请的内存大小就超过了系统总内存。Heuristic的意思是“试探
式的”,内核利用某种算法(对该算法的详细解释请看文末)猜测你的内存申请是否合理,它认为不合理就会拒绝
overcommit。
1 – Always overcommit. 允许overcommit,对内存申请来者不拒。
2 – Don’t overcommit. 禁止overcommit。
关于禁止overcommit (vm.overcommit_memory=2) ,需要知道的是,怎样才算是overcommit呢?kernel
设有一个阈值,申请的内存总数超过这个阈值就算overcommit,在/proc/meminfo中可以看到这个阈值的大
小:
     # grep -i commit /proc/meminfo
     # grep的-i选项:忽略字符大小写的差别。
     CommitLimit:     5967744 kB
     Committed_AS:    5363236 kB

     CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。这个阈值是如何计算出来的呢?它既不是物理内存的大小,也不是free memory的大小,它是通过内核参数 vm.overcommit_ratio或vm.overcommit_kbytes间接设置的,公式如下:
           CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap

     注:
     vm.overcommit_ratio 是内核参数,缺省值是50,表示物理内存的50%。可以通过cat /proc/sys/vm/overcommit_ratio查看。如果你不想使用比率,也可以直接指定内存的字节数大小,通过另一个内核参数 vm.overcommit_kbytes 即可;如果使用了hugepages,那么需要从物理内存中减去,公式变成:
     CommitLimit = ([total RAM] – [total huge TLB RAM]) * vm.overcommit_ratio / 100 + swap
     参见https://access.redhat.com/solutions/665023

     /proc/meminfo中的 Committed_AS 表示所有进程已经申请的内存总大小,(注意是已经申请的,不是已经分配的),如果Committed_AS 超过 CommitLimit 就表示发生了 overcommit,超出越多表示 overcommit 越严重。Committed_AS 的含义换一种说法就是,如果要绝对保证不发生OOM (out of memory) 需要多少物理内存。

     当oom-killer发生时,linux会选择杀死哪些进程 选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。 点数越高,这个进程越有可能被杀死。 每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。
 

修改内核参数的三种方式

有三种方式修改内核参数,但要有root权限:
 (1)永久有效:编辑/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p使配置文件生效
 (2)暂时有效:sysctl vm.overcommit_memory=1 注意=前后没有空格
 (3)暂时有效echo 1 > /proc/sys/vm/overcommit_memory。注意1后面有空格,没有空格会导致修改失败

(3)/proc讲解

      proc是Linux系统下一个很重要的目录。它跟/etc, /home等这些系统目录不同,它不是一个真正的文件系统,而是一个虚拟的文件系统。它不存在于磁盘,而是存在于系统内存中,难怪文件夹的名字叫做proc进程。所以当你使用 ls -al /proc这条命令来查看proc目录时,会看到其下面的所有文件的大小都为0字节。 proc以文件系统的方式为访问系统内核的操作提供接口。很多系统的信息,如内存使用情况, cpu使用情况,进程信息等等这些信息,都可以通过查看/proc下的对应文件来获得。proc文件系统是动态从系统内核读出所需信息的。

/proc 目录下的文件很多,举几个例子
查看proc目录下的文件的内容,如   #cat /proc/devices
/proc/cpuinifo CPU的信息(型号、家族、缓存大小等)
/proc/meminfo物理内存、交换空间
/proc/mounts      已加载的文件系统的列表
/proc/devices 可用设备的列表,这个文件列出字符和块设备的主设备号,以及分配到这些设备号的设备

(4)区别

     sudo : 暂时切换到超级用户模式以执行超级用户权限,提示输入密码时该密码为当前用户的密码,而不是超级账户的密码。不过有时间限制,Ubuntu默认为一次时长15分钟。
     su : 切换到某某用户模式,提示输入密码时该密码为切换后账户的密码,用法为“su - 账户名称”,-后有空格,如果后面不加账户时系统默认为root账户,密码也为超级账户的密码。没有时间限制。
     sudo -i: 为了频繁的执行某些只有超级用户才能执行的权限,而不用每次输入密码,可以使用该命令。提示输入密码时该密码为当前账户的密码。没有时间限制。执行该命令后提示符变为“#”而不是“$”。想退回普通账户时可以执行“exit”或“logout” 。

     Linux的#和$区别
       【#】代表 root权限
       【$】代表普通用户
     如果更改了/etc/profile , 或~/.bashrc等文档,可以用任何符号来代替它们。linux窗口下的【root@locate~】其中的【~】代表代表用户的家目录(root为/root,一般user则为/home/username);【./】和【.】代表当前目录;【../】代表上级目录

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fang·up·ad

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值