JVM的粗浅理解

 

JVM的回收器:

    年轻代适用并发收集,年老代适用串行收集

    1.串行收集器:只适用小数据量;

    2.并行收集器:吞吐量优先;到达一定的吞吐量为目的;适用于科学技术和后台处理 ;

    3.并发收集器:响应时间优先;主要保证系统的响应时间,减少垃圾收集时的停顿时间;适用于应用服务器,电信领域;

调优总结:

    年轻代大小选择:

        响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制;

        吞吐量优先的应用:尽可能设置大,可到达Gbit的程度;

    年老代大小选择:

        响应时间优先的应用:考虑并发会话率和会话持续时间;小了会造成内存碎片,高回收频率以及应用暂停;大了则需要较长的收集时间;

 

HASHMAP

    Treemap和LinkedHashMap如何保证顺序的?

        Treemap通过实现SortMap接口,能够把它保存的键值对根据key排序,基于红黑树,从而保证Treemap中所有键值对处于有序状态;

        LinkedHashMap则是通过插入排序(put的时候顺序是什么,取出来的就是什么顺序)和访问排序(改变顺序把访问过的放到底部)让键值有序

Hashtable为什么不能接受null建和值?

        因为equals()方法需要对象,不能为空

减少碰撞的方法?

    扰动函数;原理:如果两个不相等的对象返回不同的hashcode,那么碰撞的几率就会小,这意味着存链表结构减小,取值时就不用频繁调用equals方法,提高HashMap的性能(扰动即Hash方法内部的算法实现,目的是让不同对象返回不同的hashcode)

    使用不可变,声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生。不可变性能够缓存不同建的hashcode,将提高整个获取对象的速度。

为什么选择红黑树而不用二叉查找树?

    二叉查找树在特殊情况下回变成线性结构;引入红黑树是为了查找数据快,解决链表查询深度的问题;

红黑树?

    1.每个节点非黑即红;2.根节点总是黑色的;3.如果节点是红色的,则它的子节点是黑色的(反之不一一定);4.每个叶子节点都是黑色的空节点(NIL节点);5.从根节点到业绩诶单或空子节点的每条路径,必须包含相同数据的黑色节点(既相同的黑色高度)

 

    hshtable默认容量:11

    ConcurrentHashMap原理: 引入了CAS(内存值和预期值相同时,才会更改为新值)对sizeCtl的控制都是由CAS来实现的。默认为0;

CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。

它涉及到三个操作数:内存值、预期值、新值,当且仅当预期值和内存值相等时才将内存值修改为新值。这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一样,如不一样则表示期间此内存值已经被别的线程更改过,舍弃本次操作,否则说明期间没有其他线程对此内存值操作,可以把新值设置给此块内存。

        对变量增加一个版本号,每次修改,版本号+1,比较的时候比较版本号;

    ConcurrentHashMap同步性能更好,因为仅仅根据同步级别对map的一部分进行上锁;

当HashTable的大小增加到一定程度时,性能下降,因为迭代时需要被锁定很长时间。(锁定整个map)

   而ConcurrentHashMap引入了分割,无论多大,仅仅需要锁定map的某个部分,而其他的线程不需要等到迭代完整才能访问map。

 

 

垃圾回收机制GC

1.引用计数法:计数器为0则表示可回收(若存在循环引用对象,会导致无法回收)

2.可达性分析算法:通过一系列GC root对象作为起始点,向下搜索,将其走过的路径成为引用链,当一个对象到GC root没有引用链相连时,证明不可用。

垃圾回收器特点:

    1.serial (新生代-串行-收集器)

        策略:标记-复制-清除;优点:简单搞笑,适合Client模式的桌面应用;缺点:多核环境下无法充分利用资源;

    2.parnew(新生代-并行-收集器)

        策略:标记-复制-清除  优点:多线程,独占式,多核环境下提高CPU利用率; 缺点:但和环境下比serial效率低

    3.parallel scanvenge(新生代-并行-收集器)

        策略:标记-复制-清除; 优点:精准控制“吞吐量”,gc时间。吞吐量=执行用户代码时间/(执行用户代码时间+内存回收时间);   配置参数(精准调控);

    4.serial old(老年代-串行-收集器)

        策略:标记-清除-整理;

    5.parall old(老年代-并发-收集器)

    6.cms (老年代-并发-收集器)

    :策略:标记-清除; 优点:“停顿时间”最短;   缺点:内存碎片; 适用场景:互联网Web应用的server端,涉及用户交互,响应速度快;

    7。G1(新生代&老年代-并行&并发-服务端收集器)

        策略:G1将内存划分为Region,避免内存碎片;

 

 

 

 

 

链接:https://www.zhihu.com/question/27339390/answer/36511809
 




JVM体系结构
    JVM中主要包括(PC寄存器,栈)(堆,方法区)本地方法区
    


    一个Java线程中,包括PC寄存器(保存当前执行的指令位置)
                        栈帧(一个方法对应一个栈帧)
                        本地变量(保存计算中的临时数据)


    当一个线程结束后,线程内所有数据就自动销毁,不需要垃圾回收。
    
    方法区是堆的一部分,就是Java的永久区PermGen


        JVM方法区是用于存储类结构信息的地方,一个class文件会被解析成JVM能识别的几个部分,这些不同的部分在这个class被加载到JVM时,
        会被存储在不同的数据结构中,其中的常量池、域、方法数据、方法体、构造函数,包括类中的专用方法、实例初始化、接口初始化都存储在这个区域。
        
        方法区这个存储区域也属于Java堆中的一部分,也就是Java堆中的永久区。这个区域可以被所有的线程共享,并且它的大小可以通过参数来设置。
        
    本地方法区是为了JVM运行native方法(C/C++等语言写的方法)准备的区域。


内存分配方式
    静态内存分配:
        在编译时确定需要的内存空间,当程序加载时系统把内存一次性分配给它。
        静态内存分配是在Java栈上分配的,当方法结束时对应的栈帧也就撤销,所以分配的内存空间就被回收
    动态内存分配:
        程序执行时才知道要分配的内存大小,当对象创建的时候,在堆上给对象分配一块空间,空间回收时间不定,由JVM垃圾回收器管理。






java堆(JavaHeap)
1.用来存放对象的,几乎所有对象都放在这里,被线程共享的,或者说是被栈共享的
2.堆又可以分为新生代和老年代,实际还有一个区域叫永久代,但是jdk1.7已经去永久代了,所以可以当作没有,永久代是当jvm启动时就存放的JDK自身的类和接口数据,关闭则释放。新生代可以分为Eden区和两个幸存区,这么设计是为了更好地利用内存  之前的设计是只分为两部分一样一半  后来发现这样只利用到了一半的内存  才改为按比例分成三个区的,使用的是复制回收算法,两个幸存区是较小的区域。逻辑是每次使用Eden区和其中一个幸存区,回收时将其还存活着的对象一次性的复制到另一个幸存区中,最后清理到刚才使用的Eden和其中一个幸存区。。新建对象就在Eden区,Eden就是伊甸,顾名思义。但是并不是对象最活跃的区域,对象最活跃的区域是老年代,因为经过各种垃圾回收之后对象都跑到这里来了。
3.内存溢出内存溢出其实没什么好讲的,满了就会溢出。怎么才能满呢,不断创建对象,那问题又来了,创建多了被回收怎么办,好办,将新建的对象存到list里去,就不会回收了,为什么呢,因为jvm判定一个对象的死活就是根据对象是不是被引用。
此外堆跟随jvm的,有jvm就有堆。堆也是垃圾回收的主要区域,又叫GC堆,垃圾堆,玩笑。jvm


默认下,新生代 ( Young ) = 1/3 的堆空间大小,老年代 ( Old ) = 2/3 的堆空间大小;


新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。默认的,Edem : from : to = 8 : 1 : 1;


JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间;


GC 分为两种:老生代中采用标记-清除算法的Full GC ( 或称为 Major GC )和新生代中采用复制算法的Minor GC。新生代是 GC 收集垃圾的频繁区域;



1.要说栈是用来存什么的,其实我感觉不严谨,栈是运行时创建的,是跟随线程的,它不是用来存什么的,那它用来干什么的,它是用来存栈帧的,没有图不太好说呢,等下我去截个图。






图来了我就不用多说了。每个栈帧其实可以理解为一个方法,我是这么理解的,之间的关系就是调用。
2.栈的好处就是不需要垃圾回收,随着线程结束内存就释放。
3.但是并不是说就不会内存溢出,那么栈的内存溢出是怎么产生的呢,肯定也是满了,这个满了怎么理解呢,一是要申请的不够了,二是jvm内存太小,这是个有趣的问题。但是产生的错误却是不一样的,如果创建一个void方法调用自身,错误是stackoverflowError,如果不断创建线程则会outOfMemoryError。这里就有一个比较高级的问题了,对于第二种多线程内存溢出该怎么解决呢,深入理解jvm一书中给出的解决方案是这样的,通过减小最大堆和栈容量来换取更多的线程。


方法区和运行时常量池
1.方法区是堆的一个逻辑区域,但是又叫非堆。运行时常量池又是方法区的一部分,真正的一部分。方法区并不是存方法的,存方法的应该是栈或者栈帧。方法区存的是类信息、常量、静态变量等,也是被线程共享的区域。运行时常量池存放的是编译期生产的各种字面量和符号引用。
2.这块内存区域的回收没啥好说的,因为我也不太清楚,我只知道HotSpot的设计团队选择把GC分代扩展至方法区了,或者是使用永久代实现方法区。
3.内存是肯定会溢出的,不断创建类会导致方法区内存溢出,而不断将常量放入常量池(String.intern()),常量池也会内存溢出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值