1.Algorithm
8.21 号 LeetCode 每日一题:2337. 移动片段得到字符串
详情请看:2337. 移动片段得到字符串 - 力扣(LeetCode)
方法:通过双指针方法,同时遍历字符串 start 和 target。规定 L 只能往左移动,R 只能往右移动。假设指针在 start 中的位置为 i,指针在 target 中的位置为 j,可得到以下两条规则:
- 字符为
L时,只有当i>=j时,字符L才能从start中位置i往左移动,达到target中的位置j - 字符为
R时,只有当i<=j时,字符R才能从start中位置i往右移动,达到target中的位置j
由于只有移动的位置为 _ 时,才能移动,可知每个字符的移动都是根据顺序移动的,即 start 和 target 中去除 _ 后,应该相同,所以我们可以同时从左往右遍历字符串,碰到 L 或 R 后进行判断,如果读取到字符不相同时,则说明不符合条件。
案例执行过程:
代码:
public boolean canChange(String start, String target) {
int n = start.length();
int i = 0,j = 0;
while(i<n && j<n){
while(i<n && start.charAt(i)=='_') i++;
while(j<n && target.charAt(j)=='_') j++;
if(i<n && j<n){
if(start.charAt(i) != target.charAt(j)){
return false;
}
char c = start.charAt(i);
// 当字符为 L 时,只能往左移动,则 start 中的位置 i 必 大于等于 target 中的 j 位置
// 当字符为 R 时,只能往右移动,则 start 中的位置 i 必 小于等于 target 中的 j 位置
if((c=='L' && i<j) || (c=='R' && i>j)){
return false;
}
i++;
j++;
}
}
// start:"R_____"
// end:"_____R"
while(i < n){
if(start.charAt(i) != '_'){
return false;
}
i++;
}
// start:"_____L"
// end:"L_____"
while(j < n){
if(target.charAt(j) != '_'){
return false;
}
j++;
}
return true;
}
2.Review
Chapter 1. Introduction (oracle.com)
阅读了 《Java 虚拟机规范》 引言部分。
1.简史
- Java 编程语言是一种通用的、并发的、面向对象的语言。它的语法类似于 C 和 C + + ,但是它忽略了使 C 和 C + + 复杂、混乱和不安全的许多特性。
- Java 平台最初是为了解决联网的客户端之间构建软件的问题。要求支持多主机架构,并能够安全交付。因此它需具备编译后的代码能够在网络传输中存活下来,能够在任何客户端上运行,还要确保客户端运行的安全性。
- 网络的 HTML 文档格式所支持的内容有限。而 Java 语言和平台的特性,使得在 HTML 页面嵌入程序成为可能。
- 包含 Java 平台的 Web 浏览器能够提供动态的内容,同时确保内容的安全性。
2.Java 虚拟机
- Java 虚拟机是 Java 平台的基石,它保证了语言的可移植性特点和保护用户免受恶意程序。
- Java 虚拟机是一种抽象计算机。像真正的计算机一样,它有一个指令集,并在运行时操作各种内存区域
- Java 虚拟机对 Java 语言一无所知,只知道一种特定的二进制格式,即 .class 字节码。意味着其他语言只要能够编译成符合虚拟机规范的 .class 字节码文件,都可以通过虚拟机运行程序。
3.Techniques/Tips
学习 JVM 的垃圾回收算法,总结了 3 种常见的算法。
1.标记-清除算法
执行过程:
当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除:
- 标记:Collector 从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的 Header 中记录为可达对象
- 清除:Collector 对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收
缺点:
- 标记清除算法的效率不算高
- 在进行 GC 的时候,需要停止整个应用程序,用户体验较差
- 这种方式清理出来的空闲内存是不连续的,产生内碎片,需要维护一个空闲列表
何为清除?
这里的清除并不是直接置空,而是把这块空间加入空间列表中。下次有新对象需要加载的时候直接覆盖。
2.复制算法
核心思想:
- 将内存空间分为两块,每次只使用其中一块
- 在垃圾回收时,将正在使用的内存空间中的存活的对象复制到空闲的那块内存空间中中
- 清除正在使用的内存空间中的所有对象
- 交换两个内存的角色
优点:
- 没有标记和清除过程,实现简单,运行高效
- 复制过去以后保证空间的连续性,不会出现“碎片”问题
缺点:
- 需要两倍的内存空间
- 对于G1这种分拆成为大量 region 的 GC,复制而不是移动,意味着 GC 需要维护 region 之间对象引用关系,不管是内存占用或者时间开销也不小
注意:
如果存活对象比较多的情况下,其效率很低。当内存中大部分对象都是垃圾对象时,其执行效率较高。比较适合于新生代区的垃圾回收。
3.标记-压缩(整理)算法
执行过程:
- 标记:从引用根节点开始遍历,标记所有被引用的对象
- 压缩:将所有存活的对象压缩到内存的一端,按顺序存放
- 清除边界外所有空间
优点:
- 内存空间连续,没有碎片问题
- 消除了复制算法中,内存减半的高额代价
缺点:
- 从效率上来说,低于复制算法
- 移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址
- 移动过程中,需要全程暂停用户应用程序,即:STW
| 标记-清除算法 | 复制算法 | 标记-整理算法 | |
|---|---|---|---|
| 效率 | 较低 | 高 | 低 |
| 空间开销 | 少(内存不连续,存在碎片问题) | 多(需要2倍空间,不存在碎片问题) | 少(不存在碎片问题) |
| 移动对象 | 否 | 是 | 是 |
4.Share
98 | 高效学习:深度,归纳和坚持实践 (geekbang.org)