java的一些基础知识回顾

简介: 🎄JVM线程私有和共享的区域🎇线程上下文切换🍒如何判断对象是否存活🍸引用计数法🎍可达性分析法🍖JVM中的垃圾回收算法🧃标记清除算法🥫复制算法🥓标记整理算法🍨如何判断变量是否线程安全🍻最长递增子序列🎄JVM线程私有和共享的区域

JVM线程私有的区域有:虚拟机栈,本地方法栈,程序计数器。

虚拟机栈:主要存储方法,局部变量,运行的数据。
本地方法栈:主要存储本地方法(含有Native关键字的方法)。
程序计数器:存储程序运行位置的字节码行号指示器。

JVM线程共享的区域有:Java堆,元空间

Java堆:存储所有创建的对象,数组等。
元空间:存储虚拟机加载的字节码数据,常量,静态变量,运行时常量池等。

🎇线程上下文切换
线程上下文切换,也就是CPU不再执行当前的线程,而去执行其他的线程。那有哪些原因会导致线程的上下文切换呢?

线程的时间片用完
垃圾回收(会暂停当前工作的线程,先进行垃圾回收)
更高优先级的线程运行
线程主动调用了某些方法,如sleep,yeild,wait,join,synchronized,lock等
当发生上下文切换时,操作系统会保存当前线程的状态,恢复另一个线程的状态,此时程序计数器会记住下一条jvm指令的执行地址,同时上文记录,程序计数器是线程私有的。

🍒如何判断对象是否存活
判断对象是否存活有两种方法:引用计数算法和可达性分析算法。

🍸引用计数法
在对象被创建的时候,会在对象头中分配一个空间,即计时器,来保存这个对象被引用的次数。如果这个对象被其他的对象引用,它的引用计数器会+1,如果删除其他对象对这个对象的引用,则它的引用计数会-1,当对象的引用计数为0时,这个对象就会被当成垃圾回收。

优点:
引用计数法实现起来比较简单,判断对象是否存活的效率比较高。
缺点:
无法解决对象之间循环引用的问题,不能检测到环的出现。例如,A和B之间相互引用,此时计数器都会显示为1,此时A和B都无法进行垃圾回收。

🎍可达性分析法
Java虚拟机中的垃圾回收机制都是采用的可达性分析算法来探索存活的对象的。此种方法工作原理是会扫描java堆中的对象,沿着GC Roots对象往下寻找,看看是否能在此引用链中找到该对象,如果找不到的话,证明该对象没用了,表示该对象可以回收。

可达性分析算法最大的优点之一就是解决了对象之间的相互循环依赖的问题,目前和引用计数法比起来没有缺点。

🍖JVM中的垃圾回收算法
对于新生代和老年代的对象,在JVM中会采取不同的垃圾回收算法。年轻代的对象一般都是朝生暮死的,创建之后很快就会被回收,而老年代的对象是需要长期存活的,因此用到的算法大不相同。新生代对应的收集方法为“Minor GC”,老年代对应的收集方法称为“Major GC”,而对于整个堆空间和方法区的回收被称为“Full GC”

🧃标记清除算法
标记清除算法为最基础的垃圾收集算法,即为每个对象都分配一个标记为,这个标记位会记录对象的状态。标记着所要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以标记存活的对象,清理掉未标记的对象。标记清除算法用于老年代的垃圾回收中。

优点: 基于可达性分析算法,实现起来比较简单,后续的算法都是基于这种思想来实现的。
缺点:
影响最大的一点在于,标记清除算法会使内存空间碎片化,即标记并清除垃圾后,会产生很多不连续的内存空间,这将导致较大的对象因为无法找到连续的内存而提前触发一次垃圾回收。

如果大部分对象需要回收,就会进行大量的标记和清除操作,存活对象数量多时效率会降低。

🥫复制算法
复制算法被用于新生代的垃圾回收机制中,新生代有三部分,Eden(80%),和两个survivor区(From Survivor 和 To Survivor)。两个Survivor区为容量大小相等的两块内存,每次只使用其中的一块内存,当使用的那块内存用完后,就会将内存中还存活着的对象复制到另一块内存上,然后把使用过的那块内存空间清空。

优点: 实现起来比较简单,效率也比较高,可以保证内存有连续的区域,能够解决标记清除算法导致的内存碎片问题。
缺点:
可分配的内存空间缩小了一半儿,代价比较高,内存空间浪费比较多; 存活的对象比较多的时候使用复制算法将会导致效率降低。
进行标记清除算法时,会导致应用程序挂起(停顿),即stop the world(STW)。

扩展:
90%以上的对象都是朝生暮死的,所以在新生代中,每次为对象分配内存时会使用Eden区和其中的一块Survivor区,当发生垃圾回收时,JVM会将Eden和Survivor中存活的对象都复制到另一块Survivor区域内,之后清理掉Eden区和Survivor区域中的空间。综上所述,建立新对象时,新生代可用内存空间为整个儿新生代容量的90%(80%的Eden区和10%的Survivor区),如果发生了极少部分情况,即多于10%的对象存活下来了,没有被垃圾回收器回收掉,此时JVM会触发空间担保机制,即当Survivor空间不足以容纳一次Minor GC后的存活对象时,就需要依赖老年代进行分配担保。

🥓标记整理算法
标记整理法是对标记清除算法的一个改进。第一个阶段和标记清除算法一样,都是将对象标记为存活和死亡状态,然而在第二阶段,标记清除算法只是将被标记的对象进行清除,标记整理算法会将存活的对象进行整理并且放到另一个端,然后再把所有的对象清除掉。
标记整理算法用于老年代的回收机制中。

优点: 不会像垃圾清除算法那样产生不连续的内存碎片空间
不会像复制算法那样划分两个区域,提高了空间的利用率
缺点:
效率上肯定更慢一些,因为多了一步整理的操作过程。

🍨如何判断变量是否线程安全
对于成员变量和静态变量

如果它们没有被共享,则它们是线程安全的;
如果它们被共享了,根据它们的状态是否能够改变,又会分两种情况:如果只有读操作,则它们是线程安全的;如果有读写操作,则这段代码是临界区,是需要考虑线程安全的。
对于局部变量是否线程安全

局部变量是线程安全的
但局部变量引用的对象则未必线程安全。如果该对象没有逃离方法的作用访问,它是线程安全的;如果该对象逃离方法的作用范围,则是需要考虑线程安全的。

🍻最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

思路:该题让求出最长的递增子序列,因此至少需要一次遍历,考虑到代码进行到每一步的状态才可以,所以动态规划法解决此题比较容易。

代码+详解:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0){
            return 0; //长度为0直接返回0
        }
        int [] dp=new int[nums.length];
        dp[0]=1; //初始化数组,也可以调用库函数Arrays.fill(dp,1),但是效率会慢些
        int result=1;//初始化结果
        for(int i=1;i<nums.length;i++){
            dp[i]=1;//初始化数组
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                dp[i]=Math.max(dp[i],dp[j]+1);//循环更新dp[i]的最大值
                }
            }
            result=Math.max(result,dp[i]);
        }
        return result;
    }
}

相关文章
|
5月前
|
安全 Java 编译器
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
39 1
|
6月前
|
Java 调度 开发者
Java中的多线程编程:基础知识与实践
【5月更文挑战第29天】 在现代软件开发中,多线程编程是一个不可忽视的领域。特别是在Java这种广泛使用的编程语言中,掌握多线程的概念和技术对于开发高效、响应迅速的应用程序至关重要。本文将深入探讨Java多线程的核心概念、实现机制以及常见问题的解决方案。我们将从基础出发,逐步揭示如何通过多线程提升程序性能,并且讨论并发编程中的挑战和解决策略。文章的目的是为开发者提供一个清晰的多线程编程指南,帮助他们在实际项目中有效地应用这些知识。
37 3
|
6月前
|
Java 开发者
Java中的多线程编程:基础知识与实践
【5月更文挑战第26天】 在现代计算机科学中,多线程编程是一个复杂且强大的概念。Java作为一种广泛使用的编程语言,其对多线程的支持使得开发者能够创建出更为复杂、高效和响应迅速的应用程序。本文将深入探讨Java中的多线程编程,从基础概念到实际编程技巧,为读者提供全面的理解和实践指导。
|
6月前
|
监控 IDE Java
Java中的多线程编程:基础知识与实践
【5月更文挑战第30天】 在现代软件开发中,多线程编程是提高应用程序性能和响应能力的关键。特别是在Java这样的多平台、多用途的编程语言中,掌握多线程技术至关重要。本文旨在为读者提供有关Java多线程编程的全面介绍,从基本概念到高级应用技巧,帮助初学者和有经验的开发者理解并有效使用多线程。文章不仅涉及基础理论,还包括代码示例和最佳实践,确保读者能够将知识应用于实际项目中。
|
6月前
|
Java 程序员 调度
Java中的多线程编程:基础知识与实践
【5月更文挑战第28天】 在现代计算机科学中,多线程编程是一个重要的概念,它允许程序同时执行多个任务。在Java中,多线程编程是通过创建并管理线程来实现的。本文将介绍Java中多线程编程的基础知识,包括线程的概念、创建和控制线程的方法,以及线程同步和通信的相关技术。通过阅读本文,读者将对Java中的多线程编程有一个全面的了解,并能在实际编程中应用这些知识。
|
6月前
|
安全 Java 数据安全/隐私保护
Java中的多线程编程:基础知识与实践
【5月更文挑战第24天】 在现代软件开发中,多线程编程是提升应用性能和响应速度的关键技术之一。Java 作为一种广泛使用的编程语言,其内置的多线程功能为开发者提供了强大的并发处理能力。本文将深入探讨 Java 多线程的基础概念、实现机制以及在实际开发中的应用。我们将从线程的创建和管理出发,逐步讲解同步机制、死锁问题以及如何利用高级并发工具有效地构建稳定、高效的多线程应用。通过理论结合实例的方式,旨在帮助读者掌握 Java 多线程编程的核心技能,并在实际项目中灵活运用。
|
6月前
|
安全 Java
Java基础知识面试总结(全面,实时更新)
Java基础知识面试总结(全面,实时更新)
|
6月前
|
Java 程序员 调度
Java中的多线程编程:基础知识与实践
【5月更文挑战第19天】多线程编程是Java中的一个重要概念,它允许程序员在同一时间执行多个任务。本文将介绍Java多线程的基础知识,包括线程的创建、启动和管理,以及如何通过多线程提高程序的性能和响应性。
|
6月前
|
Java
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
|
6月前
|
Java
Java中的多线程编程:基础知识与实践
【5月更文挑战第13天】在计算机科学中,多线程是一种使得程序可以同时执行多个任务的技术。在Java语言中,多线程的实现主要依赖于java.lang.Thread类和java.lang.Runnable接口。本文将深入探讨Java中的多线程编程,包括其基本概念、实现方法以及一些常见的问题和解决方案。