赞
踩
在JVM中,每个线程有自己的虚拟机栈,而整个JVM实例共享一些内存区域。JVM的内存划分主要包括四个部分:程序计数器、虚拟机栈、堆区和方法区(元数据区)。
程序计数器:程序计数器用于存储当前线程所执行的字节码指令的地址。在程序执行过程中,程序计数器会随着指令的执行而递增,如果遇到条件分支、循环等控制结构时,程序计数器的值也会发生变化。
虚拟机栈:每个线程都有自己的虚拟机栈,栈内存储的是栈帧。栈帧包含方法的局部变量表、操作数栈、动态链接、方法出口等信息。每当一个方法被调用时,JVM都会在虚拟机栈中创建一个新的栈帧。当方法执行结束时,栈帧会被弹出。
堆区:堆区是JVM内存中最大的一块区域,用于存储通过new
关键字创建的对象和数组。所有线程共享堆区,是垃圾回收器管理的主要区域。
方法区(元数据区):方法区用于存储类的元数据、常量、静态变量和即时编译后的代码。虽然方法区是堆的一部分,但它有一个别名叫做“非堆”。
需要注意的是,程序计数器和虚拟机栈是线程私有的,而堆区和方法区是所有线程共享的。
- class Student {
- // 类的定义
- }
-
- public class People {
- public static void main(String[] args) {
- Student s = new Student();
- // s变量是一个局部变量,位于虚拟机栈中
- // s指向的Student对象位于堆区
- }
- }
在上述代码中,s
变量是一个局部变量,位于虚拟机栈中。s
指向的Student
对象实际存储在堆区中。
Java代码需要解析为.class文件,并通过JVM读取.class文件创建类对象,将其加载到方法区。类加载可以细分为以下五个步骤:加载、验证、准备、解析和初始化。
加载:将.class文件中的二进制数据加载到JVM中,将这些数据映射为方法区中的类对象。这一步通常包括从文件系统、网络或其他源读取.class文件。
验证:验证.class文件的字节码是否符合JVM的规范,确保没有安全威胁或格式错误。这一步的主要目的是保护虚拟机免受恶意代码的攻击。
准备:为类的静态变量分配内存,并将其初始化为默认值(通常为零或null)。此时只分配内存,不进行任何赋值操作。
解析:将常量池中的符号引用转换为直接引用。符号引用是用来表示类、接口、字段和方法的符号表项,通过解析将其转换为具体的内存地址或偏移量。符号引用在类加载时并不会直接指向具体内存,解析步骤将其替换为可以直接访问的内存地址或偏移量。
初始化:对类的静态变量赋予初始值,并执行静态初始化块。如果类中定义了静态变量且有初始值,则在此阶段赋予变量这些初始值。随后,执行类的静态代码块(如果有),以完成类的初始化工作。
通过上述五个步骤,JVM能够确保.class文件中的字节码被正确加载并转换为可以执行的Java类,确保代码的安全性和正确性。
双亲委派模型是Java虚拟机(JVM)类加载机制中使用的一种模式。这种模型有助于保证核心类的安全性,防止类的重复加载,并且实现分层管理。
保证核心类的安全性:类加载过程会优先由父类加载器进行。根类加载器(Bootstrap ClassLoader)会首先尝试加载Java的核心类库,这样可以确保这些核心类不会被其他恶意类加载器篡改。
防止类的重复加载:当一个类加载器尝试加载某个类时,它会首先检查缓存区是否已经加载过该类。如果缓存中不存在该类,类加载请求会逐级向上委派给父类加载器,直到到达根类加载器(Bootstrap ClassLoader)。如果根类加载器已经加载过该类,它将直接返回该类的引用。否则,类加载过程会返回一个ClassNotFoundException,通知子类加载器继续尝试加载。
分层管理:通过使用双亲委派模型,可以实现类加载的分层管理。自定义类和核心类等会被不同的类加载器加载,避免了相互干扰。核心类由根类加载器加载,而自定义类通常由应用程序类加载器或自定义类加载器加载。
具体过程如下:类加载请求首先会被委派给父类加载器,直至到达根类加载器(Bootstrap ClassLoader)。如果根类加载器能够找到对应的类,则进行加载并返回。如果根类加载器找不到该类,会抛出ClassNotFoundException,并将异常传递给子类加载器,子类加载器依次尝试加载该类,直到由自定义类加载器进行处理。
通过这种方式,双亲委派模型有效地确保了Java应用程序中类加载的安全性、可靠性和灵活性。
JVM的垃圾回收机制(GC)是用来管理内存的分配和释放的。当GC发现不再被使用的对象(垃圾)时,会自动释放它们占用的内存空间。
JVM会从GC Roots(例如静态变量、栈中的引用、本地方法栈中的引用等)开始,对所有对象进行扫描,以确定哪些对象是可达的(仍被使用的),哪些对象是不可达的(垃圾)。
JVM的堆分为以下几部分:
新生代(Young Generation):
老年代(Old Generation):存放从新生代中筛选出来的、多次GC后仍未被清除的长生命周期对象。
元空间(Metaspace,JDK 8及以后):存放类的元数据,如类定义、方法、静态数据等信息。JDK 8之前称为永久代(Permanent Generation)。
标记-清除(Mark-Sweep):
复制算法(Copying):
标记-整理(Mark-Compact):
通过这些方式,JVM的垃圾回收机制能够有效管理内存,自动释放不再使用的对象,确保内存资源的合理利用。不同的GC算法和策略有其适用场景和优缺点,开发者可以根据具体应用需求选择合适的GC策略。
this关键字在Java中用于引用当前对象。它有几个不同的用法,具体取决于上下文:
this关键字主要用于以下几个方面:
区分局部变量和成员变量: 在方法或构造方法中,局部变量和成员变量如果同名,可以使用this关键字来区分它们。
调用类的其他构造方法: 在一个构造方法中,可以通过this关键字调用同一类中的其他构造方法,以减少代码重复。
返回当前对象: this关键字还可以用来返回当前对象的引用,通常用于链式调用。
总之,this关键字在Java中是一个非常重要的概念,它的正确使用可以使代码更加清晰和易于维护。
匿名对象是指没有被任何变量引用的对象。这种对象在创建后立即使用,并在使用完毕后被垃圾回收。
使用匿名对象调用方法: 匿名对象可以直接用于调用方法,而无需先声明一个变量来引用该对象。
使用匿名对象作为参数传入方法: 匿名对象可以直接作为参数传递给方法,这在需要临时对象时非常有用。
匿名对象作为方法的返回值: 方法可以返回一个匿名对象,在需要立即使用返回对象而不需要长期保存引用时非常方便。
匿名对象的使用使得代码更加简洁,特别是在仅需要临时对象的场景中。由于这些对象没有被任何变量引用,它们在使用后会很快被垃圾回收,从而提高内存利用效率。
Spring是面向企业级的Java开发框架,其主要特点是IOC(控制反转)、DI(依赖注入)和面向切面的编程。由于基于Java的特性,Spring具有广泛的部署平台。
Spring 常见的注解:
@Component:用于标记一个类,使其能够被 Spring 容器管理。所有其他的 Spring 注解(例如 @Service、@Controller 等)都基于 @Component,因此它是这些注解的基础。
@Controller:用于标记一个类,使其能够被 Spring MVC 框架管理,通常用于定义控制器类。
@Service:用于标记一个类,表示该类是处理业务逻辑的服务组件。
@Repository:用于标记一个类,表示该类是数据访问组件,通常用于与数据库交互。
@PathVariable:用于从 URL 路径中动态获取变量值,用于 RESTful 风格的接口中。
@Bean:用于方法上,表示该方法的返回值应该注册为 Spring 容器中的一个 Bean,以便于依赖注入。
Spring 框架采用了 Inversion of Control(IoC)模式来实现对象的创建和管理,并通过 Dependency Injection(DI)来进行对象的依赖注入。利用 Aspect-Oriented Programming(AOP)技术,Spring 使得业务逻辑和系统服务(如事务管理、日志记录等)能够分离,从而简化了业务处理的实现。
Spring 集成了许多流行的框架,例如 MyBatis,提供了更强大的数据持久化功能和更灵活的 SQL 映射配置。
此外,Spring 还提供了便捷的测试方案,使得开发者能够更方便地编写单元测试和集成测试,提高了代码的可维护性和可靠性。
Servlet的生命周期主要包含三个阶段:初始化、服务和销毁。以下是对每个阶段的详细说明:
web.xml
中的<load-on-startup>
元素配置加载方式:
init()
方法doGet()
、doPost()
等)destroy()
方法通过这个生命周期,Servlet能够高效地处理客户端请求,并合理管理资源。
Forward和Redirect都用于在服务器处理后将请求或响应转发到另一个资源。
setAttribute()
设置数据。getAttribute()
获取数据。getParameter()
: 获取客户端发送的数据。getAttribute()
: 获取服务器端设置的数据。getParameter()
: 用于接收客户端输入。getAttribute()
: 用于服务器端不同处理阶段之间的数据共享。getParameter()
: 仅在当前请求中有效。getAttribute()
: 可以在整个请求处理过程中共享。假设有一个登录功能:
- // 获取客户端参数
- String username = request.getParameter("username");
- String password = request.getParameter("password");
-
- // 验证逻辑...
-
- // 设置属性以供后续使用
- request.setAttribute("user", authenticatedUser);
- // 获取之前设置的属性
- User user = (User) request.getAttribute("user");
这样,getAttribute()
允许我们在整个请求处理过程中传递和共享数据,而不仅限于初始的客户端参数。
JSP包含分为两种:静态包含(编译时包含)和动态包含(运行时包含)。它们在工作方式和执行时间上有所不同。
示例应用:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。