diff --git a/prdoc/pr_5644.prdoc b/prdoc/pr_5644.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..3300d557fce40f274120fb76b449e675eccb2a2f
--- /dev/null
+++ b/prdoc/pr_5644.prdoc
@@ -0,0 +1,8 @@
+title: 'pallet-utility: Improve weight annotations'
+doc:
+- audience: Runtime Dev
+  description: |-
+    Prevent allocations when calculating the weights.
+crates:
+- name: pallet-utility
+  bump: patch
diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs
index 3ce5b4ff86496d48ffa60d953b86d56880ec65c5..ed5544fe55ca4f28007c9f16bc95987e570a70bf 100644
--- a/substrate/frame/utility/src/lib.rs
+++ b/substrate/frame/utility/src/lib.rs
@@ -74,7 +74,7 @@ pub use pallet::*;
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
-	use frame_support::pallet_prelude::*;
+	use frame_support::{dispatch::DispatchClass, pallet_prelude::*};
 	use frame_system::pallet_prelude::*;
 
 	#[pallet::pallet]
@@ -183,21 +183,8 @@ pub mod pallet {
 		/// event is deposited.
 		#[pallet::call_index(0)]
 		#[pallet::weight({
-			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
-			let dispatch_weight = dispatch_infos.iter()
-				.map(|di| di.weight)
-				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
-				.saturating_add(T::WeightInfo::batch(calls.len() as u32));
-			let dispatch_class = {
-				let all_operational = dispatch_infos.iter()
-					.map(|di| di.class)
-					.all(|class| class == DispatchClass::Operational);
-				if all_operational {
-					DispatchClass::Operational
-				} else {
-					DispatchClass::Normal
-				}
-			};
+			let (dispatch_weight, dispatch_class) = Pallet::<T>::weight_and_dispatch_class(&calls);
+			let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch(calls.len() as u32));
 			(dispatch_weight, dispatch_class)
 		})]
 		pub fn batch(
@@ -233,13 +220,13 @@ pub mod pallet {
 					// Take the weight of this function itself into account.
 					let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32);
 					// Return the actual used weight + base_weight of this call.
-					return Ok(Some(base_weight + weight).into())
+					return Ok(Some(base_weight.saturating_add(weight)).into())
 				}
 				Self::deposit_event(Event::ItemCompleted);
 			}
 			Self::deposit_event(Event::BatchCompleted);
 			let base_weight = T::WeightInfo::batch(calls_len as u32);
-			Ok(Some(base_weight + weight).into())
+			Ok(Some(base_weight.saturating_add(weight)).into())
 		}
 
 		/// Send a call through an indexed pseudonym of the sender.
@@ -305,21 +292,8 @@ pub mod pallet {
 		/// - O(C) where C is the number of calls to be batched.
 		#[pallet::call_index(2)]
 		#[pallet::weight({
-			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
-			let dispatch_weight = dispatch_infos.iter()
-				.map(|di| di.weight)
-				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
-				.saturating_add(T::WeightInfo::batch_all(calls.len() as u32));
-			let dispatch_class = {
-				let all_operational = dispatch_infos.iter()
-					.map(|di| di.class)
-					.all(|class| class == DispatchClass::Operational);
-				if all_operational {
-					DispatchClass::Operational
-				} else {
-					DispatchClass::Normal
-				}
-			};
+			let (dispatch_weight, dispatch_class) = Pallet::<T>::weight_and_dispatch_class(&calls);
+			let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32));
 			(dispatch_weight, dispatch_class)
 		})]
 		pub fn batch_all(
@@ -359,7 +333,7 @@ pub mod pallet {
 					// Take the weight of this function itself into account.
 					let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32);
 					// Return the actual used weight + base_weight of this call.
-					err.post_info = Some(base_weight + weight).into();
+					err.post_info = Some(base_weight.saturating_add(weight)).into();
 					err
 				})?;
 				Self::deposit_event(Event::ItemCompleted);
@@ -414,21 +388,8 @@ pub mod pallet {
 		/// - O(C) where C is the number of calls to be batched.
 		#[pallet::call_index(4)]
 		#[pallet::weight({
-			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
-			let dispatch_weight = dispatch_infos.iter()
-				.map(|di| di.weight)
-				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
-				.saturating_add(T::WeightInfo::force_batch(calls.len() as u32));
-			let dispatch_class = {
-				let all_operational = dispatch_infos.iter()
-					.map(|di| di.class)
-					.all(|class| class == DispatchClass::Operational);
-				if all_operational {
-					DispatchClass::Operational
-				} else {
-					DispatchClass::Normal
-				}
-			};
+			let (dispatch_weight, dispatch_class) = Pallet::<T>::weight_and_dispatch_class(&calls);
+			let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::force_batch(calls.len() as u32));
 			(dispatch_weight, dispatch_class)
 		})]
 		pub fn force_batch(
@@ -494,6 +455,27 @@ pub mod pallet {
 			res.map(|_| ()).map_err(|e| e.error)
 		}
 	}
+
+	impl<T: Config> Pallet<T> {
+		/// Get the accumulated `weight` and the dispatch class for the given `calls`.
+		fn weight_and_dispatch_class(
+			calls: &[<T as Config>::RuntimeCall],
+		) -> (Weight, DispatchClass) {
+			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info());
+			let (dispatch_weight, dispatch_class) = dispatch_infos.fold(
+				(Weight::zero(), DispatchClass::Operational),
+				|(total_weight, dispatch_class): (Weight, DispatchClass), di| {
+					(
+						total_weight.saturating_add(di.weight),
+						// If not all are `Operational`, we want to use `DispatchClass::Normal`.
+						if di.class == DispatchClass::Normal { di.class } else { dispatch_class },
+					)
+				},
+			);
+
+			(dispatch_weight, dispatch_class)
+		}
+	}
 }
 
 /// A pallet identifier. These are per pallet and should be stored in a registry somewhere.